Bài 10: Rewrite và Redirects trong NGINX
Bài học về Rewrite và Redirects trong Nginx - rewrite rules với regex, return vs rewrite directive, location matching patterns (exact, prefix, regex), try_files directive và conditional redirects. Hướng dẫn URL manipulation, SEO-friendly redirects và best practices cho production environments.
1. Rewrite Rules với Regex
Rewrite rules cho phép modify URLs trước khi xử lý requests.
1.1. Cú pháp Rewrite
rewrite regex replacement [flag];
# regex: Regular expression pattern to match
# replacement: New URL
# flag: Optional flag (break, last, redirect, permanent)
Flags:
break- Stop processing, use rewritten URIlast- Stop processing rewrite rules, re-search locationsredirect- Return 302 temporary redirectpermanent- Return 301 permanent redirect
1.2. Basic Rewrite Examples
server {
listen 80;
server_name example.com;
# Simple rewrite
rewrite ^/old-page$ /new-page permanent;
# Rewrite with regex capture
rewrite ^/user/(.*)$ /profile/$1 permanent;
# Multiple captures
rewrite ^/post/([0-9]+)/([a-z]+)$ /article/$2/$1 permanent;
location / {
root /var/www/html;
}
}
Example với captures:
server {
# /product/123 → /item.php?id=123
rewrite ^/product/([0-9]+)$ /item.php?id=$1 last;
# /category/electronics/page/5 → /cat.php?name=electronics&page=5
rewrite ^/category/([^/]+)/page/([0-9]+)$ /cat.php?name=$1&page=$2 last;
# /blog/2024/12/my-post → /blog.php?year=2024&month=12&slug=my-post
rewrite ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ /blog.php?year=$1&month=$2&slug=$3 last;
}
1.3. Rewrite Flags
break flag:
location /api/ {
# Rewrite và dừng processing
rewrite ^/api/v1/(.*)$ /v1/$1 break;
# Continue với rewritten URI
proxy_pass http://backend;
}
# Request: /api/v1/users
# Rewritten: /v1/users
# Proxied to: http://backend/v1/users
last flag:
server {
# Rewrite và re-search locations
rewrite ^/old/(.*)$ /new/$1 last;
location /new/ {
# This location will be matched after rewrite
root /var/www/html;
}
}
# Request: /old/page
# Rewritten: /new/page
# Re-search locations, matches /new/
redirect flag (302):
server {
# Temporary redirect
rewrite ^/temp/(.*)$ /permanent/$1 redirect;
}
# Returns HTTP 302 Found
# Browser redirects to new URL
permanent flag (301):
server {
# Permanent redirect (SEO-friendly)
rewrite ^/old-site/(.*)$ https://newsite.com/$1 permanent;
}
# Returns HTTP 301 Moved Permanently
# Search engines update their index
1.4. Regex Patterns
Common patterns:
server {
# Match digits
rewrite ^/product/([0-9]+)$ /item/$1 last;
# Match letters
rewrite ^/user/([a-z]+)$ /profile/$1 last;
# Match alphanumeric
rewrite ^/page/([a-zA-Z0-9]+)$ /content/$1 last;
# Match anything except slash
rewrite ^/category/([^/]+)$ /cat/$1 last;
# Match with specific length
rewrite ^/code/([0-9]{6})$ /verify/$1 last;
# Optional parts
rewrite ^/blog/([0-9]+)(/.*)?$ /post/$1$2 last;
# Case-insensitive (use ~* in location)
location ~* ^/PAGE/(.*)$ {
rewrite ^ /page/$1 permanent;
}
}
Special characters:
# Escape special chars: . * + ? [ ] ^ $ ( ) { } | \
# Match literal dot
rewrite ^/file\.txt$ /document.txt last;
# Match query string (use $args)
rewrite ^/search$ /search.php?$args last;
# Preserve query string
rewrite ^/old$ /new permanent; # Automatically preserves ?param=value
1.5. Multiple Rewrites
server {
listen 80;
server_name example.com;
# Sequential rewrites
rewrite ^/products/(.*)$ /items/$1 last;
# This won't execute because of 'last' flag above
rewrite ^/items/(.*)$ /products/$1 last;
location /items/ {
root /var/www/html;
}
}
Correct way for chained rewrites:
server {
# First rewrite
location /old/ {
rewrite ^/old/(.*)$ /temp/$1 last;
}
# Second rewrite
location /temp/ {
rewrite ^/temp/(.*)$ /new/$1 last;
}
# Final location
location /new/ {
root /var/www/html;
}
}
1.6. Conditional Rewrites
server {
# Rewrite based on conditions
# If not HTTPS, redirect
if ($scheme != "https") {
rewrite ^ https://$host$request_uri permanent;
}
# If mobile user agent
if ($http_user_agent ~* (mobile|android|iphone)) {
rewrite ^/$ /mobile/ last;
}
# If specific referer
if ($http_referer ~* google.com) {
rewrite ^/landing$ /landing-google last;
}
# Multiple conditions
set $redirect 0;
if ($scheme != "https") {
set $redirect 1;
}
if ($host !~* ^www\.) {
set $redirect "${redirect}1";
}
if ($redirect = "11") {
rewrite ^ https://www.$host$request_uri permanent;
}
}
2. Return vs Rewrite Directive
2.1. Return Directive
Return is faster and more efficient than rewrite for simple redirects.
server {
listen 80;
server_name example.com;
# Simple return
location /old-page {
return 301 /new-page;
}
# Return with variable
location /redirect {
return 301 https://example.com$request_uri;
}
# Return with custom response
location /api/health {
return 200 "OK\n";
}
# Return JSON
location /api/status {
default_type application/json;
return 200 '{"status":"ok","timestamp":"$time_iso8601"}';
}
}
2.2. Return Status Codes
server {
# 301 - Permanent redirect (SEO)
location /old {
return 301 /new;
}
# 302 - Temporary redirect
location /temp {
return 302 /temporary-location;
}
# 307 - Temporary redirect (preserves method)
location /preserve {
return 307 /new-location;
}
# 308 - Permanent redirect (preserves method)
location /permanent-preserve {
return 308 /new-permanent;
}
# 200 - Success
location /ok {
return 200 "Success";
}
# 204 - No Content
location /nocontent {
return 204;
}
# 400 - Bad Request
location /bad {
return 400 "Bad Request";
}
# 403 - Forbidden
location /forbidden {
return 403 "Access Denied";
}
# 404 - Not Found
location /notfound {
return 404 "Not Found";
}
# 500 - Internal Server Error
location /error {
return 500 "Internal Server Error";
}
# 503 - Service Unavailable
location /maintenance {
return 503 "Under Maintenance";
}
}
2.3. Return vs Rewrite Performance
# GOOD - Fast, efficient
location /old-page {
return 301 /new-page;
}
# SLOWER - Regex processing overhead
location /old-page {
rewrite ^/old-page$ /new-page permanent;
}
# GOOD - Simple return for redirects
if ($host != "www.example.com") {
return 301 https://www.example.com$request_uri;
}
# SLOWER - Regex rewrite
if ($host != "www.example.com") {
rewrite ^ https://www.example.com$request_uri permanent;
}
2.4. When to Use Each
Use return when:
- Simple redirects
- No regex needed
- Better performance required
- Returning custom content
Use rewrite when:
- Need regex capture groups
- Complex URL transformations
- Need to preserve parts of URL
- Using flags (break, last)
Examples:
server {
# Use return - simple redirect
location = /about {
return 301 /about-us;
}
# Use rewrite - need regex captures
location /product/ {
rewrite ^/product/([0-9]+)$ /item.php?id=$1 last;
}
# Use return - redirect to external URL
location /blog {
return 301 https://blog.example.com;
}
# Use rewrite - transform URL structure
location /old-api/ {
rewrite ^/old-api/v1/(.*)$ /api/v2/$1 break;
proxy_pass http://backend;
}
}
3. Location Matching
Location matching xác định cách Nginx xử lý different URL patterns.
3.1. Location Modifiers
# No modifier - Prefix match
location /images/ {
# Matches: /images/*, /images/photo.jpg
}
# = - Exact match (highest priority)
location = /about {
# Matches: /about only
# NOT: /about/, /about?page=1
}
# ^~ - Prefix match, stop regex checking
location ^~ /static/ {
# Matches: /static/*
# Stops checking regex locations
}
# ~ - Case-sensitive regex
location ~ \.php$ {
# Matches: .php files (case-sensitive)
# Matches: file.php, script.PHP (no)
}
# ~* - Case-insensitive regex
location ~* \.(jpg|png|gif)$ {
# Matches: .jpg, .JPG, .Jpg (all cases)
}
3.2. Location Priority Order
Priority (highest to lowest):
- Exact match
= - Prefix match with
^~ - Regular expression
~or~*(first match wins) - Prefix match (longest match wins)
Example:
server {
listen 80;
# Priority 1 - Exact match
location = /test {
return 200 "Exact match: /test\n";
}
# Priority 2 - Prefix (stop regex)
location ^~ /test {
return 200 "Prefix ^~: /test*\n";
}
# Priority 3 - Regex
location ~* ^/test {
return 200 "Regex ~*: /test*\n";
}
# Priority 4 - Prefix
location /test {
return 200 "Prefix: /test*\n";
}
# Default
location / {
return 200 "Default: /\n";
}
}
Test results:
curl http://localhost/test
# → "Exact match: /test"
curl http://localhost/test123
# → "Prefix ^~: /test*" (if ^~ exists)
# → "Regex ~*: /test*" (if no ^~)
curl http://localhost/other
# → "Default: /"
3.3. Prefix Matching
server {
# Longest prefix wins
location /api/ {
return 200 "API root\n";
}
location /api/v1/ {
return 200 "API v1\n";
}
location /api/v2/ {
return 200 "API v2\n";
}
}
# /api/test → "API root"
# /api/v1/users → "API v1"
# /api/v2/products → "API v2"
3.4. Regex Matching
server {
# Case-sensitive regex
location ~ ^/API/ {
return 200 "Uppercase API\n";
}
# Case-insensitive regex
location ~* ^/api/ {
return 200 "Any case API\n";
}
# File extension regex
location ~ \.(jpg|png|gif)$ {
return 200 "Image file\n";
}
# Complex regex
location ~ ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ {
return 200 "Blog post: $1-$2-$3\n";
}
}
3.5. Nested Locations
server {
location /api/ {
# Outer location
# Nested location
location ~ \.json$ {
# Matches: /api/*.json
return 200 "JSON API\n";
}
location ~ \.xml$ {
# Matches: /api/*.xml
return 200 "XML API\n";
}
# Default for /api/
return 200 "API default\n";
}
}
3.6. Named Locations
server {
location / {
try_files $uri $uri/ @backend;
}
# Named location (cannot be accessed directly)
location @backend {
proxy_pass http://backend_server;
}
# Another example
error_page 404 = @notfound;
location @notfound {
return 404 "Custom 404 page\n";
}
}
3.7. Complete Location Example
server {
listen 80;
server_name example.com;
root /var/www/html;
# Exact match - homepage
location = / {
index index.html;
}
# Exact match - specific file
location = /favicon.ico {
log_not_found off;
access_log off;
}
# Prefix match with stop regex
location ^~ /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Regex - PHP files
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Regex - Image files
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public";
}
# Prefix match - API
location /api/ {
proxy_pass http://api_backend/;
}
# Default location
location / {
try_files $uri $uri/ =404;
}
# Deny hidden files
location ~ /\. {
deny all;
}
}
4. Try_files Directive
Try_files checks for files in order and uses the first one found.
4.1. Basic Try_files
location / {
# Try file, then directory, then 404
try_files $uri $uri/ =404;
}
# Request: /page
# Tries:
# 1. /page (file)
# 2. /page/ (directory with index)
# 3. Returns 404
4.2. Try_files với Fallback
# Single Page Application (SPA)
location / {
try_files $uri $uri/ /index.html;
}
# Request: /about
# Tries:
# 1. /about (file)
# 2. /about/ (directory)
# 3. /index.html (fallback)
# WordPress/PHP applications
location / {
try_files $uri $uri/ /index.php?$args;
}
4.3. Try_files với Named Location
location / {
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://backend_server;
}
# Request: /api/users
# Tries:
# 1. /api/users (file)
# 2. /api/users/ (directory)
# 3. @backend (proxy to backend)
4.4. Multiple Fallbacks
location / {
# Try multiple files
try_files $uri $uri/index.html $uri.html @backend;
}
location @backend {
proxy_pass http://backend;
}
# Request: /page
# Tries:
# 1. /page
# 2. /page/index.html
# 3. /page.html
# 4. @backend
4.5. Try_files with Custom Response
location / {
try_files $uri $uri/ /404.html =404;
}
# Or return custom content
location / {
try_files $uri $uri/ =404;
error_page 404 = @notfound;
}
location @notfound {
return 404 "File not found\n";
}
4.6. Try_files Patterns
Static site:
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
React/Vue/Angular SPA:
location / {
root /var/www/app;
try_files $uri $uri/ /index.html;
}
WordPress:
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
API with fallback:
location /api/ {
# Try cached response, then proxy
try_files /cache$uri @api;
}
location @api {
proxy_pass http://api_backend;
}
Multiple static directories:
location /assets/ {
# Try multiple roots
try_files /public$uri /static$uri =404;
}
5. Conditional Redirects
5.1. If Directive
# IMPORTANT: If is evil in Nginx (use sparingly)
# Prefer map, return, or rewrite when possible
server {
# Redirect non-www to www
if ($host !~* ^www\.) {
return 301 https://www.$host$request_uri;
}
# Redirect HTTP to HTTPS
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# Redirect based on user agent
if ($http_user_agent ~* (mobile|android|iphone)) {
return 302 /mobile;
}
# Redirect old domain
if ($host = old.example.com) {
return 301 https://new.example.com$request_uri;
}
}
5.2. Map-based Redirects
http {
# Map for redirects (better than if)
map $request_uri $redirect_uri {
/old-page /new-page;
/old-about /about-us;
/old-contact /contact;
default "";
}
server {
if ($redirect_uri != "") {
return 301 $redirect_uri;
}
}
}
5.3. Geo-based Redirects
http {
geo $country {
default US;
192.0.2.0/24 UK;
198.51.100.0/24 JP;
}
map $country $redirect_domain {
US example.com;
UK uk.example.com;
JP jp.example.com;
}
server {
listen 80;
if ($host != $redirect_domain) {
return 302 https://$redirect_domain$request_uri;
}
}
}
5.4. Time-based Redirects
http {
map $time_iso8601 $maintenance {
default 0;
# Maintenance window: 2024-12-03 02:00 to 04:00
~^2024-12-03T0[2-3] 1;
}
server {
if ($maintenance) {
return 503;
}
error_page 503 /maintenance.html;
location = /maintenance.html {
root /var/www/errors;
}
}
}
5.5. Argument-based Redirects
server {
# Redirect based on query parameter
if ($arg_lang = "fr") {
return 302 /fr$request_uri;
}
if ($arg_lang = "es") {
return 302 /es$request_uri;
}
# Redirect if parameter missing
if ($arg_id = "") {
return 400 "ID parameter required";
}
}
5.6. Complex Conditions
server {
# Multiple conditions (AND logic)
set $redirect 0;
if ($scheme = http) {
set $redirect "${redirect}1";
}
if ($host !~* ^www\.) {
set $redirect "${redirect}1";
}
if ($redirect = "11") {
return 301 https://www.$host$request_uri;
}
}
6. Real-world Examples
6.1. SEO-friendly Redirects
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# HTTP to HTTPS (301 permanent)
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
# www to non-www (301 permanent)
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
# Old URLs to new structure
rewrite ^/blog/([0-9]+)/([0-9]+)/(.+)$ /articles/$1-$2-$3 permanent;
# Product URLs
rewrite ^/product-([0-9]+)\.html$ /products/$1 permanent;
# Category URLs
rewrite ^/cat-([a-z-]+)$ /category/$1 permanent;
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
6.2. Migration from Old Site
http {
# Bulk redirects using map
map $request_uri $new_uri {
/old-about.html /about;
/old-contact.html /contact;
/products.html /shop;
/blog/post1.html /blog/post-1;
/blog/post2.html /blog/post-2;
# ... hundreds more
default "";
}
server {
listen 443 ssl http2;
server_name example.com;
# Apply bulk redirects
if ($new_uri != "") {
return 301 $new_uri;
}
# Pattern-based redirects
rewrite ^/news/([0-9]+)$ /articles/$1 permanent;
rewrite ^/downloads/(.+)\.zip$ /files/$1 permanent;
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
}
6.3. API Versioning
server {
listen 80;
server_name api.example.com;
# Redirect v1 to v2
location /v1/ {
rewrite ^/v1/(.*)$ /v2/$1 permanent;
}
# Current API version
location /v2/ {
proxy_pass http://api_backend/;
}
# Default to latest version
location / {
rewrite ^/(.*)$ /v2/$1 last;
}
}
6.4. Multi-language Sites
http {
# Detect language from Accept-Language
map $http_accept_language $lang {
default en;
~*^fr fr;
~*^es es;
~*^de de;
}
server {
listen 80;
server_name example.com;
# Redirect to language-specific subdomain
location = / {
return 302 https://$lang.example.com;
}
# Or redirect to language path
# location = / {
# return 302 /$lang/;
# }
}
server {
listen 80;
server_name en.example.com;
root /var/www/en;
}
server {
listen 80;
server_name fr.example.com;
root /var/www/fr;
}
}
6.5. Mobile Redirect
http {
# Detect mobile devices
map $http_user_agent $is_mobile {
default 0;
~*mobile 1;
~*android 1;
~*iphone 1;
~*ipad 1;
}
server {
listen 80;
server_name example.com;
# Redirect mobile users
if ($is_mobile) {
return 302 https://m.example.com$request_uri;
}
location / {
root /var/www/html;
}
}
server {
listen 80;
server_name m.example.com;
root /var/www/mobile;
}
}
6.6. Maintenance Mode
server {
listen 80;
server_name example.com;
root /var/www/html;
# Enable maintenance mode
set $maintenance off;
# Whitelist admin IPs
if ($remote_addr ~ "^(192\.168\.1\.|10\.0\.0\.)") {
set $maintenance off;
}
# Check if maintenance file exists
if (-f /var/www/maintenance.flag) {
set $maintenance on;
}
if ($maintenance = on) {
return 503;
}
error_page 503 @maintenance;
location @maintenance {
rewrite ^(.*)$ /maintenance.html break;
}
location / {
try_files $uri $uri/ =404;
}
}
6.7. A/B Testing
http {
# Split traffic 50/50
split_clients "${remote_addr}${http_user_agent}" $variant {
50% "a";
* "b";
}
server {
listen 80;
server_name example.com;
location / {
if ($variant = "a") {
rewrite ^ /variant-a last;
}
if ($variant = "b") {
rewrite ^ /variant-b last;
}
}
location /variant-a {
root /var/www/test-a;
}
location /variant-b {
root /var/www/test-b;
}
}
}
7. Troubleshooting
7.1. Rewrite Loop Detection
# BAD - Creates infinite loop
location / {
rewrite ^(.*)$ /index.php last;
}
location /index.php {
rewrite ^(.*)$ / last;
}
# Request: /page
# Rewrite: /index.php
# Rewrite: /
# Rewrite: /index.php
# ... infinite loop (Nginx stops after 10 cycles)
Fix:
# GOOD - Use break or proper conditions
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
}
7.2. Debug Rewrites
# Enable rewrite log
error_log /var/log/nginx/error.log notice;
rewrite_log on;
server {
location / {
rewrite ^/test/(.*)$ /result/$1 last;
return 200 "Original URI: $uri\n";
}
}
# Check logs:
# tail -f /var/log/nginx/error.log
7.3. Test Redirects
# Test with curl
curl -I http://example.com/old-page
# Follow redirects
curl -L http://example.com/old-page
# Verbose output
curl -v http://example.com/old-page
# Check redirect chain
curl -IL http://example.com/old-page
7.4. Common Issues
Issue 1: Query string not preserved
# BAD - Loses query string
rewrite ^/old$ /new;
# GOOD - Preserves query string automatically
rewrite ^/old$ /new permanent;
# Or explicitly:
rewrite ^/old$ /new?$args permanent;
Issue 2: Wrong flag causes issues
# BAD - Uses 'last' in if
if ($host = old.com) {
rewrite ^ https://new.com$request_uri last;
}
# GOOD - Use return or permanent
if ($host = old.com) {
return 301 https://new.com$request_uri;
}
Issue 3: Regex not matching
# BAD - Missing anchors
rewrite /old /new permanent;
# Matches: /old, /cold, /folder/old
# GOOD - Use anchors
rewrite ^/old$ /new permanent;
# Matches: /old only
# Or use exact location
location = /old {
return 301 /new;
}
8. Best Practices
8.1. Prefer return over rewrite
# GOOD - Fast and clear
location /old {
return 301 /new;
}
# AVOID - Slower, less clear
location /old {
rewrite ^/old$ /new permanent;
}
8.2. Use exact matches when possible
# GOOD - Fastest matching
location = /about {
return 301 /about-us;
}
# SLOWER - Regex overhead
location ~ ^/about$ {
return 301 /about-us;
}
8.3. Avoid if when possible
# BAD - Using if
if ($request_uri ~ ^/old) {
rewrite ^ /new permanent;
}
# GOOD - Use location
location ^~ /old {
rewrite ^/old(.*)$ /new$1 permanent;
}
# BETTER - Use return
location /old {
return 301 /new;
}
8.4. Use map for bulk redirects
# GOOD - Efficient for many redirects
http {
map $request_uri $redirect_uri {
/page1 /new1;
/page2 /new2;
/page3 /new3;
# ... hundreds more
}
server {
if ($redirect_uri != "") {
return 301 $redirect_uri;
}
}
}
# BAD - Many individual rewrites
server {
rewrite ^/page1$ /new1 permanent;
rewrite ^/page2$ /new2 permanent;
rewrite ^/page3$ /new3 permanent;
# ... hundreds more
}
8.5. Document complex rewrites
server {
# Redirect old blog structure to new
# Old: /blog/2024/12/post-title
# New: /articles/2024-12-post-title
rewrite ^/blog/([0-9]{4})/([0-9]{2})/(.+)$ /articles/$1-$2-$3 permanent;
# Redirect product IDs
# Old: /product-123.html
# New: /shop/products/123
rewrite ^/product-([0-9]+)\.html$ /shop/products/$1 permanent;
}
8.6. Test thoroughly
# Create test script
#!/bin/bash
echo "Testing redirects..."
# Test 1: Old to new
curl -IL http://example.com/old-page | grep "301\|Location"
# Test 2: HTTP to HTTPS
curl -IL http://example.com | grep "301\|Location"
# Test 3: www to non-www
curl -IL https://www.example.com | grep "301\|Location"
echo "Tests complete!"
9. Bài tập Thực hành
Bài tập 1: Basic Redirects
- Create redirect từ /old-about đến /about-us
- Implement HTTP to HTTPS redirect
- Redirect www to non-www
- Test với curl
Bài tập 2: Regex Rewrites
- Rewrite /product/123 → /item/123
- Rewrite /blog/2024/12/post → /articles/2024-12-post
- Preserve query strings
- Test với curl
Bài tập 3: Location Matching
- Setup exact match cho /
- Setup prefix match cho /api/
- Setup regex match cho image files
- Test priority order
Bài tập 4: Try_files
- Setup SPA với try_files fallback
- Configure WordPress-style try_files
- Add custom 404 page
- Test với various URLs
Bài tập 5: Bulk Redirects
- Create map với 10+ redirects
- Implement map-based redirect logic
- Test all redirects
- Measure performance vs individual rewrites
Bài tập 6: Complex Scenario
- Migrate old site structure to new
- Implement mobile detection
- Add maintenance mode
- Setup proper error pages
- Test thoroughly
Tổng kết
Trong bài này, bạn đã học:
- ✅ Rewrite rules với regex và flags
- ✅ Return vs rewrite performance
- ✅ Location matching patterns và priority
- ✅ Try_files directive và use cases
- ✅ Conditional redirects
- ✅ Real-world redirect scenarios
- ✅ Troubleshooting và best practices
Key takeaways:
- Prefer
returnoverrewritefor simple redirects - Use exact matches when possible
- Avoid
ifdirective when alternatives exist - Use
mapfor bulk redirects - Test redirects thoroughly
- Document complex rewrite rules
Bài tiếp theo: Nginx với Application Stack - PHP-FPM configuration, Nginx + Node.js, Python (uWSGI/Gunicorn), Docker containers và WebSocket proxying để integrate Nginx với various backend technologies.