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.
Related: How to enable or disable Apache modules
Related: How to test Apache configuration
Related: How to manage the Apache web server service
$ 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.
$ 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.
<VirtualHost *:80> ServerName example.com DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
<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>
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.
$ 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.
Related: How to test Apache configuration
$ sudo systemctl reload apache2
Use sudo systemctl reload httpd on systems that name the service httpd.
$ 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.
$ 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.