Custom response headers let you attach routing metadata, caching hints, or integration flags to a response without changing application code. That makes them useful when you need to prove which tier served a request, expose a narrowly scoped diagnostic value, or send a site-wide policy from the web server itself.

Apache adds or changes response headers through mod_headers and the Header directive. The directive can live in the main server config, a specific VirtualHost, a Directory block, or .htaccess when FileInfo overrides are allowed, and the action keyword decides whether Apache replaces, appends, merges, or removes the header before the response is sent.

The clearest place to start is the target site's VirtualHost so the rule stays scoped and easy to review. The steps below use the Debian and Ubuntu layout (/etc/apache2/ and the apache2 service name). If the same header is already coming from the application, a reverse proxy, or a CDN, remove or consolidate the duplicate source first, and use Header always set when the header must remain present on locally generated redirects and error responses as well as normal 200 OK responses.

Steps to add a custom response header in Apache:

  1. Open a terminal session with an account that can use sudo.
  2. Enable mod_headers on Debian or Ubuntu.
    $ sudo a2enmod headers
    Enabling module headers.
    To activate the new configuration, you need to run:
      service apache2 restart

    a2enmod enables the module by linking its files into /etc/apache2/mods-enabled. If the command says the module is already enabled, continue with the next step.

  3. Open the Apache site configuration file for the host that should send the custom header.
    $ sudoedit /etc/apache2/sites-available/example.com.conf

    Use the active VirtualHost file for the site rather than the global config when the header should apply only to one hostname or application.

  4. Locate the VirtualHost block that serves the target site.
    <VirtualHost *:80>
        ServerName example.com
        DocumentRoot /var/www/html
     
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
  5. Add the custom header inside that scope.
    <VirtualHost *:80>
        ServerName example.com
        DocumentRoot /var/www/html
     
        Header always set Example-Header "custom-value"
     
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
    </VirtualHost>
    • Replace Example-Header and custom-value with the real header name and value you need to send.
    • Header always set keeps the header on locally generated non-success responses such as redirects and 404 pages. If you want the header only on normal success responses, use Header set instead.
    • Use append or merge only when the header is meant to carry multiple values. For one final value, set is the cleaner default.

    If the same header is already generated by the application or an upstream proxy, do not blindly add a second copy. Consolidate the source or deliberately unset and replace the header in one layer.

  6. Save the configuration file.
  7. Test the Apache configuration before reloading it.
    $ sudo apache2ctl configtest
    Syntax OK

    sudo apache2ctl -t performs the same syntax check. If Apache is managed without systemd, sudo apache2ctl graceful can apply the change after the test passes.

  8. Reload Apache so the new worker processes pick up the header rule.
    $ sudo systemctl reload apache2

    Use sudo systemctl reload httpd on systems that name the service httpd.

  9. Verify that the header appears on a normal response from the target site.
    $ curl -I -H 'Host: example.com' http://127.0.0.1/
    HTTP/1.1 200 OK
    Date: Thu, 09 Apr 2026 04:37:08 GMT
    Server: Apache/2.4.58 (Ubuntu)
    Last-Modified: Thu, 09 Apr 2026 04:37:06 GMT
    ETag: "29af-64eff91906592"
    Accept-Ranges: bytes
    Content-Length: 10671
    Vary: Accept-Encoding
    Example-Header: custom-value
    Content-Type: text/html

    Use the real site URL instead of 127.0.0.1 when a reverse proxy or CDN sits in front of Apache, so you confirm the final response seen by clients.

  10. Verify that the header is still present on a locally generated non-success response when you used Header always set.
    $ curl -I -H 'Host: example.com' http://127.0.0.1/no-such-page
    HTTP/1.1 404 Not Found
    Date: Thu, 09 Apr 2026 04:37:09 GMT
    Server: Apache/2.4.58 (Ubuntu)
    Example-Header: custom-value
    Content-Type: text/html; charset=iso-8859-1

    If the header appears on the 200 OK response but not on the 404 response, the rule is probably using Header set instead of Header always set.