When setting up Apache as a reverse proxy for Jetty applications, developers often encounter unexpected 404 errors for static resources. The core issue lies in how ProxyPass !
directives interact with your directory structure and Location matching.
The configuration you provided shows a common misconception. The
block with ProxyPass !
only works if:
- Your static files are physically located in a
/static/
subdirectory - The requests exactly match the Location path
- No other directives override this exclusion
Here are three working approaches:
1. Filesystem-Based Exclusion
# Map static files directly to filesystem
Alias /static/ "/path/to/foo/static/"
Require all granted
Options -Indexes
# Proxy everything else
ProxyPass / http://localhost:8081/
ProxyPassReverse / http://localhost:8081/
2. Pattern-Based Exclusion
# Skip proxying for common static file extensions
ProxyPass !
3. Hybrid Approach (Recommended)
# Physical path for static assets
AliasMatch "^/static/(.*)" "/path/to/foo/static/$1"
Require all granted
# Proxy all non-static requests
ProxyPass / http://localhost:8081/ nocanon
ProxyPassReverse / http://localhost:8081/
# Additional exclusions
ProxyPass http://localhost:8081/
ProxyPassReverse http://localhost:8081/
Enable these Apache modules for troubleshooting:
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule headers_module modules/mod_headers.so
LoadModule rewrite_module modules/mod_rewrite.so
Add logging to track proxy behavior:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" proxy
CustomLog logs/proxy_access.log proxy
LogLevel debug
When configuring Apache as a reverse proxy for Jetty, many developers expect ProxyPass !
to completely bypass proxy processing for static files. However, there's more to it than meets the eye. Here's why your static files might still be routing to Jetty despite the exclusion rule.
The key misunderstanding lies in how Apache processes Location
versus Directory
directives. The /static/
Location block only matches requests that explicitly begin with "/static/" in the URL path.
# This ONLY matches /static/file.js but NOT /static-version/file.js
<Location /static/>
ProxyPass !
</Location>
Here's a tested configuration that properly handles static files:
<VirtualHost *:80>
ServerName foo.com
DocumentRoot "/path/to/foo"
# Static files configuration
Alias /static/ "/path/to/foo/static/"
<Directory "/path/to/foo/static">
Options -Indexes
AllowOverride None
Require all granted
</Directory>
# Proxy configuration
ProxyRequests Off
ProxyPreserveHost On
ProxyVia Off
# Explicit static files exclusion
<LocationMatch "\.(css|js|png|jpg|gif|ico|woff|woff2|ttf|svg)$">
ProxyPass !
Header set Cache-Control "max-age=31536000, public"
</LocationMatch>
# Main proxy pass
<Location />
ProxyPass http://localhost:8081/
ProxyPassReverse http://localhost:8081/
</Location>
</VirtualHost>
Case 1: Files in /static/ still proxy to Jetty
Solution: Add the Alias directive and ensure directory permissions
Case 2: Versioned static files (e.g., script-v2.js) get proxied
Solution: Use LocationMatch with regex pattern instead
Case 3: Browser caching issues
Solution: Add Cache-Control headers as shown above
For production environments, consider adding these enhancements:
# Enable HTTP/2 for static content
Protocols h2 http/1.1
# Gzip compression
AddOutputFilterByType DEFLATE text/css application/javascript
# ETag support
FileETag MTime Size
When troubleshooting, check these Apache logs:
# In your virtual host config
LogLevel debug
ErrorLog /var/log/apache2/foo-error.log
CustomLog /var/log/apache2/foo-access.log combined
Useful grep commands:
tail -f /var/log/apache2/foo-error.log | grep -i proxy
curl -I http://foo.com/static/test.js | grep -i "X-Proxy"