yum upgrades for production use, this is the repository for you.
Active subscription is required.
When users encounter a 404 “Page Not Found” or 500 “Server Error” page, showing them an error message in their preferred language significantly improves the user experience. This comprehensive guide demonstrates how to configure NGINX to serve localized error pages based on the browser’s Accept-Language header, providing a more user-friendly experience for your international visitors.
Why Use NGINX Localized Error Pages?
Serving error pages in the user’s native language offers several key benefits:
- Improved User Experience: Users can understand what went wrong without language barriers
- Lower Bounce Rates: Visitors are more likely to stay on your site when they can read error messages
- Professional Appearance: Demonstrates attention to detail and consideration for international audiences
- Better SEO: Reduces user frustration signals that search engines may factor into rankings
- Brand Trust: Users feel more confident when they see content in their language
According to various studies, over 72% of internet users prefer browsing websites in their native language. A localized error page tells your users: “We care about your experience.”
Understanding the Accept-Language Header
When a browser makes a request, it sends an Accept-Language HTTP header that indicates the user’s preferred languages. This header typically looks like:
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
This example indicates:
– ru-RU (Russian, Russia) is the most preferred (implicit q=1.0)
– ru (Russian) has a quality value of 0.9
– en-US (English, US) has a quality value of 0.8
– en (English) has a quality value of 0.7
The q parameter (quality value) ranges from 0 to 1, where higher values indicate stronger preference.
Two Methods for Detecting Browser Language
NGINX provides two effective approaches for parsing the Accept-Language header:
Method 1: Using the map Directive (No Modules Required)
The simplest approach uses NGINX’s built-in map directive with regular expressions. This method works with any standard NGINX installation and requires no additional modules.
Method 2: Using the Accept-Language Module (More Accurate)
For more accurate language detection that properly handles quality values, you can use the nginx-module-accept-language module available from the GetPageSpeed repository.
Let’s explore both methods in detail.
Method 1: NGINX Localized Error Pages Using the map Directive
This approach works with any NGINX installation and provides reliable language detection for most use cases.
Step 1: Create the Language Mapping
Add this configuration to the http block of your nginx.conf:
http {
# Parse Accept-Language header to determine user's preferred language
# The first matching pattern wins
map $http_accept_language $lang {
default en; # English as fallback
~*^ru ru; # Russian
~*^es es; # Spanish
~*^de de; # German
~*^fr fr; # French
~*^zh zh; # Chinese
~*^pt pt; # Portuguese
~*^ja ja; # Japanese
~*^ko ko; # Korean
~*^it it; # Italian
~*^nl nl; # Dutch
~*^pl pl; # Polish
~*^ar ar; # Arabic
}
# ... rest of your http configuration
}
The ~*^ru pattern means:
– ~* – case-insensitive regular expression
– ^ru – matches strings starting with “ru”
Step 2: Configure Error Pages with Language Variable
In your server block, configure the error_page directive to use the $lang variable:
server {
listen 80;
server_name example.com;
root /var/www/html;
# Localized error pages using the $lang variable
error_page 404 /errors/$lang/404.html;
error_page 500 502 503 504 /errors/$lang/50x.html;
location / {
try_files $uri $uri/ =404;
}
# Internal location for serving error pages
location /errors/ {
internal;
root /var/www/html;
# Fallback to English if the localized page doesn't exist
try_files $uri /errors/en/404.html =500;
}
}
Key configuration points:
error_pagewith variables: The NGINX error_page directive supports variables in the URI, allowing dynamic path constructioninternaldirective: Ensures error pages can only be accessed through internal redirects, not directly by userstry_filesfallback: If a translation doesn’t exist, falls back to English
Step 3: Create the Error Page Directory Structure
Create the directory structure for your localized error pages:
# Create directories for each supported language
mkdir -p /var/www/html/errors/{en,ru,es,de,fr,zh,pt,ja,ko,it,nl,pl,ar}
Step 4: Create Localized Error Page Files
Create the HTML files for each language. Here’s an example structure:
English (/var/www/html/errors/en/404.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Page Not Found</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex; justify-content: center; align-items: center;
height: 100vh; margin: 0; background: #f5f5f5; }
.container { text-align: center; padding: 40px; }
h1 { color: #333; font-size: 72px; margin: 0; }
p { color: #666; font-size: 18px; }
a { color: #007bff; text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p>The page you're looking for doesn't exist.</p>
<p><a href="/">Return to Homepage</a></p>
</div>
</body>
</html>
Russian (/var/www/html/errors/ru/404.html):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Страница не найдена</title>
<style>/* Same styles as above */</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p>Запрашиваемая страница не найдена.</p>
<p><a href="/">Вернуться на главную</a></p>
</div>
</body>
</html>
Spanish (/var/www/html/errors/es/404.html):
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Página no encontrada</title>
<style>/* Same styles as above */</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<p>La página que buscas no existe.</p>
<p><a href="/">Volver a la página principal</a></p>
</div>
</body>
</html>
Step 5: Test the Configuration
Verify your NGINX configuration syntax:
nginx -t
If successful, reload NGINX:
nginx -s reload
Test with different Accept-Language headers using curl:
# Test English (default)
curl -s -H "Accept-Language: en-US,en;q=0.9" http://localhost/nonexistent
# Test Russian
curl -s -H "Accept-Language: ru-RU,ru;q=0.9" http://localhost/nonexistent
# Test Spanish
curl -s -H "Accept-Language: es-ES,es;q=0.9" http://localhost/nonexistent
# Test fallback (unsupported language falls back to English)
curl -s -H "Accept-Language: ja-JP,ja;q=0.9" http://localhost/nonexistent
Method 2: Using the Accept-Language Module
For more sophisticated language detection that properly handles quality values and complex Accept-Language headers, use the nginx-module-accept-language module.
Installation on Rocky Linux, AlmaLinux, CentOS, RHEL
Install the module from the GetPageSpeed repository:
# Install the GetPageSpeed repository
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
# Install the accept-language module
dnf -y install nginx-module-accept-language
Enable the Module
Add the following at the top of your /etc/nginx/nginx.conf:
load_module modules/ngx_http_accept_language_module.so;
Configure with set_from_accept_language
The module provides the set_from_accept_language directive:
load_module modules/ngx_http_accept_language_module.so;
http {
server {
listen 80;
server_name example.com;
root /var/www/html;
# Use the module to detect language
# List supported languages in order of preference
set_from_accept_language $lang en ru es de fr zh pt ja;
# Localized error pages
error_page 404 /errors/$lang/404.html;
error_page 500 502 503 504 /errors/$lang/50x.html;
location / {
try_files $uri $uri/ =404;
}
location /errors/ {
internal;
root /var/www/html;
try_files $uri /errors/en/404.html =500;
}
}
}
The set_from_accept_language directive:
– Parses the full Accept-Language header
– Returns the best matching language from your supported list
– Falls back to the first language in the list if no match is found
– The first parameter ($lang) is the variable to store the result
– Subsequent parameters are the languages your site supports
Advantages of the Module Approach
Using the nginx-module-accept-language offers several benefits over the map approach:
- Proper Quality Value Handling: The module correctly interprets
qvalues to select the best matching language - Simpler Configuration: One directive instead of multiple map patterns
- More Accurate Matching: Handles complex
Accept-Languageheaders more reliably - Maintained Code: The module is actively maintained and tested
Complete Configuration Example
Here’s a production-ready configuration combining best practices:
load_module modules/ngx_http_accept_language_module.so;
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_accept_language" lang=$lang';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/html;
# Detect user's preferred language
set_from_accept_language $lang en ru es de fr zh pt ja ko it;
# Custom error pages based on language
error_page 400 /errors/$lang/400.html;
error_page 401 /errors/$lang/401.html;
error_page 403 /errors/$lang/403.html;
error_page 404 /errors/$lang/404.html;
error_page 500 502 503 504 /errors/$lang/50x.html;
# Main application
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Error pages location (internal only)
location /errors/ {
internal;
root /var/www/html;
# Comprehensive fallback chain
try_files $uri /errors/en/$uri /errors/en/404.html =500;
}
# Optional: Add language header for debugging
add_header X-Detected-Language $lang;
}
}
Localized Error Pages for Reverse Proxy Setups
When using NGINX as a reverse proxy, you need to intercept errors from the upstream server:
server {
listen 80;
server_name example.com;
set_from_accept_language $lang en ru es de fr;
location / {
proxy_pass http://backend_server;
proxy_intercept_errors on; # Intercept upstream errors
# Handle upstream errors with localized pages
error_page 500 502 503 504 /errors/$lang/50x.html;
error_page 404 /errors/$lang/404.html;
}
location /errors/ {
internal;
root /var/www/html;
try_files $uri /errors/en/404.html =500;
}
}
The proxy_intercept_errors on directive tells NGINX to handle error responses from the upstream server using the configured error_page directives.
Best Practices for Localized Error Pages
1. Always Provide a Fallback
Never assume a translation exists. Either ensure all translation exist or always configure try_files with an English fallback:
try_files $uri /errors/en/404.html =500;
2. Keep Error Pages Lightweight
Error pages should load quickly even under server stress:
– Use inline CSS instead of external stylesheets
– Avoid JavaScript dependencies
– Minimize images or use inline SVG
– Keep total page size under 50KB
3. Include Helpful Information
Good error pages should include:
– A clear explanation of what happened
– A link to the homepage
– A search box (if applicable)
– Contact information for support
4. Log the Detected Language
Add the detected language to your access log for analytics:
log_format main '$remote_addr - $lang - "$request" $status';
5. Test All Languages
After deployment, test each language:
for lang in en ru es de fr zh; do
echo "Testing $lang:"
curl -s -H "Accept-Language: $lang" http://localhost/nonexistent | head -1
done
Troubleshooting Common Issues
Error Pages Not Displaying in Correct Language
Problem: Users see English error pages regardless of their browser language.
Solutions:
1. Check that the map or set_from_accept_language directive is in the correct context (http block for map)
2. Verify the $http_accept_language variable is populated: add it to your access log
3. Ensure pattern matching is correct (case-insensitive ~*)
500 Error Instead of Custom Error Page
Problem: Server returns a generic 500 error instead of the custom page.
Solutions:
1. Verify error page files exist and have correct permissions:
bash
ls -la /var/www/html/errors/en/
chmod 644 /var/www/html/errors/en/*.html
2. Check the try_files fallback path exists
3. Review NGINX error logs: tail -f /var/log/nginx/error.log
Module Not Loading
Problem: NGINX fails to start with module error.
Solutions:
1. Verify module is installed: ls /usr/lib64/nginx/modules/ngx_http_accept_language_module.so
2. Ensure load_module is at the very top of nginx.conf, before any other directives
3. Check NGINX version compatibility with the module
Performance Considerations
The map directive and the accept-language module both have minimal performance impact:
- Map directive: Evaluated once per request, stored in a hash table
- Accept-language module: Lightweight parsing with O(n) complexity where n is the number of supported languages
For high-traffic sites, both methods handle millions of requests per second without measurable overhead.
Conclusion
Serving localized NGINX error pages is a straightforward enhancement that significantly improves the user experience for international visitors. Whether you use the built-in map directive for simplicity or the nginx-module-accept-language for more accurate language detection, the implementation requires minimal configuration changes.
Remember to:
– Create error pages for all your supported languages
– Always provide an English fallback
– Keep error pages lightweight and helpful
– Test thoroughly with different Accept-Language headers
– Monitor your access logs to understand your language distribution
By following this guide, you’ll create a more inclusive and professional web experience for users worldwide, potentially reducing bounce rates and improving overall satisfaction with your website.
