From b1dd598b2907097f743d39711026cccc99f49629 Mon Sep 17 00:00:00 2001 From: Jasper Frumau Date: Wed, 28 Oct 2026 13:34:23 +0700 Subject: [PATCH 1/2] Remove the Nginx guides, now posts on wpvilla.in --- CHANGELOG.md | 1 + CLAUDE.md | 2 +- README.md | 2 +- docs/nginx/README.md | 62 ---- docs/nginx/browser-caching/README.md | 143 --------- .../nginx-includes/all/assets-expiry.conf.j2 | 3 + docs/nginx/image-optimization/README.md | 69 ---- .../nginx-includes/webp-avf.conf.j2 | 4 + docs/nginx/redirects/README.md | 294 ------------------ .../examples/generic/ssl-redirects.conf.j2 | 1 + .../generic/wordpress-permalinks.conf.j2 | 1 + .../imagewize.com/seo-redirects.conf.j2 | 1 + 12 files changed, 13 insertions(+), 570 deletions(-) delete mode 100644 docs/nginx/README.md delete mode 100644 docs/nginx/browser-caching/README.md delete mode 100644 docs/nginx/image-optimization/README.md delete mode 100644 docs/nginx/redirects/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 51aff89..0ec1f0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- **The Nginx guides**, now two new posts and one existing post on wpvilla.in: [serving WebP and AVIF through Accept-header negotiation](https://wpvilla.in/serve-webp-avif-nginx-trellis-accept-header-bedrock-uploads/), [browser caching headers as a global include](https://wpvilla.in/nginx-browser-caching-headers-trellis-all-includes-expires/) and [the redirect provision tag and query string](https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/). The four READMEs under `docs/nginx/` are gone; the `.conf.j2` templates stay, and each now opens with a link to its post. The browser caching guide had documented 30-day and 7-day durations while the template sets one year; the post describes the template. The generic redirect best-practice and rollback sections are not carried over. - **The security guides**, now four posts on wpvilla.in: [the two scanners and checksum verification](https://wpvilla.in/wordpress-malware-scanners-targeted-general-checksum-verification/), [reading scan results](https://wpvilla.in/reading-malware-scan-results-false-positives-wordpress-hardening/), [a permanent Nginx deny list](https://wpvilla.in/nginx-deny-list-trellis-cidr-whitelist-self-lockout/) and [a Trellis security routine](https://wpvilla.in/trellis-wordpress-security-routine-abuseipdb-fail2ban/), plus the existing [fail2ban post](https://wpvilla.in/fail2ban-wordpress-wp-login-trellis-nginx-deny-list/). The `README`, `SECURITY-GUIDE` and `SCANNER-SUMMARY` guides in `wp-cli/security/` and the `README`, `FAIL2BAN` and `MANUAL-IP-BLOCKING` guides in `trellis/security/` are gone; the scanners and the AbuseIPDB scripts stay. The scanner wrapper's closing hint now names the post. - **The provisioning and updater guides**, now four posts on wpvilla.in: [a new Mac for Trellis](https://wpvilla.in/new-mac-trellis-development-setup-lima-composer-node/), [cloning an existing project](https://wpvilla.in/clone-trellis-bedrock-project-lima-vm-first-provision/), [updating Trellis](https://wpvilla.in/update-trellis-without-losing-customisations-rsync-excludes/) and [provision tags and the PHP upgrade](https://wpvilla.in/trellis-provision-tags-php-version-upgrade-checklist/). The cron guide is covered by the existing [WP-Cron interval post](https://wpvilla.in/trellis-wp-cron-interval-scheduled-posts-late-cron-interval/). `trellis/provision/` is gone, as are the updater README and `manual-update.md`; the `trellis-updater` script stays. - **The content-creation guides**, now three posts on wpvilla.in: [creating pages from block markup](https://wpvilla.in/create-wordpress-pages-block-markup-wp-cli-trellis-vm-production/), [block patterns as PHP files](https://wpvilla.in/wordpress-block-pattern-php-files-headers-escaping-images/) and [pattern layout rules](https://wpvilla.in/wordpress-block-pattern-layout-alignfull-grid-spacing-rules/). `PAGE-CREATION.md`, `PATTERN-REQUIREMENTS.md` and the folder README are gone; the scripts and the example stay. diff --git a/CLAUDE.md b/CLAUDE.md index e68ac57..1fa5795 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ This is a collection of tools, scripts, and documentation for WordPress operatio runnable commands and so have no CLI category of their own: - `docs/dev/` - Design and planning docs for wp-ops itself. New ones go here, not at the `docs/` top level - `docs/cli/` - How the `wp-ops` binary works: usage reference (categories, platforms, completion, guide search) and the trellis-cli plugin - - `docs/nginx/` - Web server configs: `browser-caching/`, `image-optimization/`, `redirects/` (guides plus the `.conf.j2` templates they describe) + - `docs/nginx/` - Web server configs: `browser-caching/`, `image-optimization/`, `redirects/` (the `.conf.j2` templates; each header links its wpvilla.in post) - `docs/troubleshooting/` - Server and WordPress troubleshooting guides (OOM, PHP-FPM, MariaDB, mail) - `docs/bedrock/` - Bedrock/Composer workflows: local package development, and the standard `wp-cli.yml` for Bedrock path setup - `docs/wordpress-utilities/` - Copy-paste-into-a-project components and reference material: `snippets/`, `speed-optimization/` diff --git a/README.md b/README.md index 555e778..62ef17d 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ sites are run with: | **Monitoring** | AI-crawler traffic, error surfacing, traffic by country, cron-scheduled reports | [→](trellis/monitoring/README.md) | | **Security** | Malware scanners with checksum verification, fail2ban, manual IP blocking, AbuseIPDB lookup | [routine](https://wpvilla.in/trellis-wordpress-security-routine-abuseipdb-fail2ban/) · [scanners](https://wpvilla.in/wordpress-malware-scanners-targeted-general-checksum-verification/) | | **Content & SEO** | Page creation, block pattern validation, redirect/schema/orphan-page audits | [SEO](wp-cli/seo/README.md) | -| **Nginx** | WebP/AVIF serving, browser caching and redirect templates for Trellis `nginx-includes/` | [images](docs/nginx/image-optimization/README.md) · [caching](docs/nginx/browser-caching/README.md) · [redirects](docs/nginx/redirects/README.md) | +| **Nginx** | WebP/AVIF serving, browser caching and redirect templates for Trellis `nginx-includes/` [images](https://wpvilla.in/serve-webp-avif-nginx-trellis-accept-header-bedrock-uploads/) · [caching](https://wpvilla.in/nginx-browser-caching-headers-trellis-all-includes-expires/) · [redirects](https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/) | | **Bedrock** | Testing unreleased packages via Composer `path` repos, standard `wp-cli.yml` | [packages](docs/bedrock/local-package-development/README.md) · [wp-cli.yml](docs/bedrock/wp-cli-config/README.md) | | **Troubleshooting** | PHP-FPM, MariaDB, OOM and mail diagnostics | [→](docs/troubleshooting/README.md) | | **Scripts & guides** | Theme sync, PR helpers, WP-CLI snippets for Bedrock | [scripts](scripts/README.md) · [snippets](docs/wordpress-utilities/snippets/README.md) | diff --git a/docs/nginx/README.md b/docs/nginx/README.md deleted file mode 100644 index 9ca3a6e..0000000 --- a/docs/nginx/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Nginx Configuration Tools - -Nginx include templates for WordPress sites on [Roots Trellis](https://roots.io/trellis/). Each guide below covers one template, how to wire it into `wordpress_sites.yml`, and how to check it deployed. - -| Guide | What it deploys | -|-------|-----------------| -| [browser-caching/](browser-caching/README.md) | Cache headers for static assets (global include) | -| [image-optimization/](image-optimization/README.md) | Automatic WebP/AVIF content negotiation (site include) | -| [redirects/](redirects/README.md) | SEO redirect templates and examples (site include) | - -## Deploying an include - -1. **Copy the template** into your Trellis project: - ```bash - # Site-specific include - cp docs/nginx/image-optimization/nginx-includes/webp-avf.conf.j2 \ - ~/trellis/nginx-includes/ - - # Global include (applies to every site on the server) - cp docs/nginx/browser-caching/nginx-includes/all/assets-expiry.conf.j2 \ - ~/trellis/nginx-includes/all/ - ``` - -2. **Reference site-specific includes** in `group_vars/production/wordpress_sites.yml`. Files under `nginx-includes/all/` need no entry: - ```yaml - wordpress_sites: - example.com: - nginx_includes: - - nginx-includes/webp-avf.conf.j2 - - nginx-includes/seo-redirects.conf.j2 - ``` - -3. **Try it in the VM first**, then deploy with the `nginx-includes` tag: - ```bash - trellis provision development - trellis vm shell -- sudo nginx -t - - trellis provision --tags nginx-includes production - ``` - -4. **Verify** with `curl -I` against a real URL (each guide lists the expected headers). - -## Scopes - -| Directory in your Trellis project | Lands on the server at | Use for | -|-----------------------------------|------------------------|---------| -| `nginx-includes/all/` | `/etc/nginx/includes.d/all/` | Caching headers, security headers, rate limits: anything every site wants | -| `nginx-includes/` | `/etc/nginx/includes.d/example.com/` | Image negotiation, per-site redirects, custom rewrites | - -All templates use the `.conf.j2` extension, so Trellis variables such as `{{ site_name }}` and Jinja2 conditionals work. Trellis validates the syntax and reloads Nginx after provisioning. - -## Troubleshooting - -1. **Configuration not applied**: confirm the file sits in the right `nginx-includes/` directory, that `wordpress_sites.yml` names it, and re-provision with `--tags nginx-includes`. -2. **Syntax errors**: run `sudo nginx -t` on the server, check the `.conf.j2` for typos and Jinja2 mistakes, and read `/var/log/nginx/error.log`. -3. **Includes not loaded**: `ssh web@example.com "grep -r 'include.*includes.d' /etc/nginx/sites-enabled/"`. -4. **Redirect loops, WebP not served, headers missing**: see the troubleshooting section of the matching guide. - -## Further reading - -- [Trellis Nginx configuration](https://roots.io/trellis/docs/nginx/) -- [Nginx documentation](https://nginx.org/en/docs/) diff --git a/docs/nginx/browser-caching/README.md b/docs/nginx/browser-caching/README.md deleted file mode 100644 index d1633de..0000000 --- a/docs/nginx/browser-caching/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Browser Caching for Trellis - -This guide covers how to implement effective browser caching for your Trellis-managed WordPress sites. Proper caching can significantly reduce page load times, bandwidth usage, and server load. - -## Table of Contents -- [Overview](#overview) -- [Nginx Configuration for Browser Caching](#nginx-configuration-for-browser-caching) -- [Implementation](#implementation) -- [File Types and Cache Duration](#file-types-and-cache-duration) -- [Testing Cache Implementation](#testing-cache-implementation) -- [Troubleshooting](#troubleshooting) - -## Overview - -Browser caching instructs visitors' browsers to store static resources locally for a specified period, reducing the need to download the same files on subsequent visits. The included Nginx configuration adds appropriate caching headers to static files such as: - -- Images (JPEG, PNG, GIF, WebP, AVIF) -- Stylesheets (CSS) -- JavaScript files -- Fonts (WOFF, WOFF2, TTF, EOT, OTF) -- SVG files -- Other static assets - -## Nginx Configuration for Browser Caching - -The `nginx-includes/all/assets-expiry.conf.j2` file in this repository contains Nginx configuration to set appropriate caching headers for various file types. The configuration uses the `expires` directive along with `Cache-Control` headers to control how long browsers should cache content. - -### How it Works - -1. The configuration maps content types to specific expiration times -2. Different file types receive appropriate cache durations -3. HTML files are not cached to ensure fresh content -4. Admin areas and PHP files are explicitly set to not cache - -## Implementation - -1. Copy the `nginx-includes` directory to your Trellis project root (same level as `server.yml`) -2. The configuration in `nginx-includes/all/assets-expiry.conf.j2` will automatically be applied to **all sites** on your server -3. Provision your environment to apply the changes: - -```bash -# For production -trellis provision --tags nginx-includes production - -# For staging -trellis provision --tags nginx-includes staging - -# For development -trellis provision --tags nginx-includes development -``` - -**Note**: Since the file is in the `all/` directory, it will be automatically included for every site configured in Trellis. You don't need to modify your `wordpress_sites.yml` configuration. - -## File Types and Cache Duration - -The configuration sets the following cache durations: - -| File Type | Cache Duration | -|-----------|---------------| -| Images (JPEG, PNG, GIF, WebP, AVIF) | 30 days | -| CSS files | 7 days | -| JavaScript files | 7 days | -| Fonts (WOFF, WOFF2, TTF, etc.) | 30 days | -| SVG files | 30 days | -| HTML files | No cache (always fresh) | -| PHP files | No cache | -| Admin areas | No cache | -| Other static files | 1 day | - -These durations provide a good balance between performance and freshness. You can adjust them in the `assets-expiry.conf.j2` file to match your specific needs. - -## Testing Cache Implementation - -After implementation, you can verify the caching headers are working correctly: - -### Using Browser Dev Tools - -1. Open your website in Chrome or Firefox -2. Open Developer Tools (F12) -3. Navigate to the Network tab -4. Reload the page -5. Select any static resource (image, CSS, or JS file) -6. Check the Response Headers section for: - - `Cache-Control` headers - - `Expires` headers - -### Using curl - -```bash -curl -I https://example.com/wp-content/uploads/image.jpg -``` - -Look for headers like: -``` -Cache-Control: public, max-age=2592000 -Expires: Wed, 17 Jun 2025 12:00:00 GMT -``` - -## Troubleshooting - -### Resources Not Caching - -1. Verify the Nginx configuration was properly included -2. Check file permissions on the configuration file -3. Test with `curl -I` to see the actual headers being sent -4. Review Nginx error logs: `/var/log/nginx/error.log` - -### Too Aggressive Caching - -If you're developing or frequently updating assets: - -1. Reduce cache durations in the configuration -2. Use versioned file names or query strings (e.g., `style.css?v=1.2`) -3. Use a cache-busting technique in your build process - -### Cached Pages Not Updating - -For WordPress pages that should always be fresh: - -1. Verify HTML pages are not being cached (they shouldn't be with this configuration) -2. Check for additional caching layers (WordPress plugins, CDN, etc.) -3. Clear browser cache during testing - -## Advanced Configuration - -You can modify the cache durations or add more specific rules by editing the `nginx-includes/assets-expiry.conf.j2` file. For example, to change the cache duration for images: - -```nginx -# Change image caching from 30 days to 14 days -~image/ 14d; -``` - -## Performance Benefits - -Implementing proper browser caching provides several benefits: - -- Reduced bandwidth usage (up to 70% on repeat visits) -- Faster page load times for returning visitors -- Reduced server load and resource consumption -- Improved Google PageSpeed scores -- Better user experience and engagement - -For optimal performance, consider combining this caching configuration with image optimization (see the image-optimization directory) and a content delivery network (CDN). \ No newline at end of file diff --git a/docs/nginx/browser-caching/nginx-includes/all/assets-expiry.conf.j2 b/docs/nginx/browser-caching/nginx-includes/all/assets-expiry.conf.j2 index dcd98e9..f44a0cc 100644 --- a/docs/nginx/browser-caching/nginx-includes/all/assets-expiry.conf.j2 +++ b/docs/nginx/browser-caching/nginx-includes/all/assets-expiry.conf.j2 @@ -1,3 +1,6 @@ +# Guide: https://wpvilla.in/nginx-browser-caching-headers-trellis-all-includes-expires/ +# Global include: no wordpress_sites.yml entry needed. + # Nginx configuration for browser caching of static assets # Settings for SVG files (moved before general static files to take precedence) diff --git a/docs/nginx/image-optimization/README.md b/docs/nginx/image-optimization/README.md deleted file mode 100644 index cd44135..0000000 --- a/docs/nginx/image-optimization/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Image Optimization Guide - -Serve WebP and AVIF versions of your images from a Trellis-managed WordPress site. The Nginx include does the serving; you (or a plugin) supply the converted files. - -## Nginx Configuration for WebP and AVIF - -The `nginx-includes/webp-avf.conf.j2` file in this repository contains Nginx configuration to automatically serve WebP or AVIF versions of images when the browser supports them. This configuration works by checking the `Accept` header from the browser and serving the appropriate image format. - -### How it Works - -When a browser requests an image: - -1. Nginx checks if the browser accepts WebP or AVIF formats -2. If supported formats exist on the server (e.g., `image.jpg.webp` or `image.jpg.avif`), Nginx serves them instead of the original -3. If no optimized version exists, the original image is served - -### Implementation - -1. Copy the `nginx-includes` directory to your Trellis project -2. Update your Trellis WordPress site configuration to include the custom Nginx configuration: - -```yaml -# group_vars/production/wordpress_sites.yml (or staging/development) -wordpress_sites: - example.com: - # ... existing configuration ... - nginx_includes: - - nginx-includes/webp-avf.conf.j2 -``` - -3. Provision your environment to apply the changes: - -```bash -# For production -trellis provision production - -# For staging -trellis provision staging - -# For development -trellis provision development -``` - -## Generating the converted files - -The include looks for the **original filename plus the new extension**: `photo.jpg` is served as `photo.jpg.webp` or `photo.jpg.avif` when both exist next to it. - -```bash -# One image -cwebp -q 80 photo.jpg -o photo.jpg.webp -cavif --quality 80 photo.jpg - -# Everything under uploads/ -find uploads/ -name "*.jpg" -exec cwebp -q 80 {} -o {}.webp \; -``` - -Showing a WebP featured image while keeping a JPG for the share card is covered in [WordPress WebP Featured Images Without Breaking og:image](https://wpvilla.in/wordpress-webp-featured-images-og-image-original-jpg/). - -Install the tools with `brew install webp cavif` (macOS) or `apt-get install webp` (Ubuntu). Quality 75-85 suits photos; use 85-95 for images with text. Keep the originals: they are the fallback. - -A plugin that writes `.jpg.webp` files on upload works with the include too, for example [WebP Express](https://wordpress.org/plugins/webp-express/), [EWWW Image Optimizer](https://wordpress.org/plugins/ewww-image-optimizer/) or [ShortPixel](https://wordpress.org/plugins/shortpixel-image-optimiser/). - -## Verify - -```bash -curl -H "Accept: image/webp" -I https://example.com/wp-content/uploads/photo.jpg -``` - -Expect a `Content-Type: image/webp` response. If you still get the JPG, check that `photo.jpg.webp` exists with exactly that name. diff --git a/docs/nginx/image-optimization/nginx-includes/webp-avf.conf.j2 b/docs/nginx/image-optimization/nginx-includes/webp-avf.conf.j2 index c7880d9..bd7e641 100644 --- a/docs/nginx/image-optimization/nginx-includes/webp-avf.conf.j2 +++ b/docs/nginx/image-optimization/nginx-includes/webp-avf.conf.j2 @@ -1,3 +1,7 @@ +# Serves .webp/.avif next to the original when the browser accepts them. +# Guide: https://wpvilla.in/serve-webp-avif-nginx-trellis-accept-header-bedrock-uploads/ +# Site include: list it under nginx_includes in wordpress_sites.yml. + map $http_accept $webp_suffix { default ""; "~*image/webp" ".webp"; diff --git a/docs/nginx/redirects/README.md b/docs/nginx/redirects/README.md deleted file mode 100644 index 2d4df29..0000000 --- a/docs/nginx/redirects/README.md +++ /dev/null @@ -1,294 +0,0 @@ -# Nginx Redirect Configuration - -Why a redirect may not deploy, and why a 301 drops the query string: [My Trellis Nginx Redirect Did Nothing](https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/). - -Trellis nginx-includes templates for managing SEO redirects and URL rewrites. - -## Overview - -This directory contains example nginx redirect configurations that can be used in Trellis projects. Redirects are managed via nginx-includes templates that are deployed during provisioning. - -## Directory Structure - -``` -redirects/ -├── README.md # This file -└── examples/ - ├── example.com/ - │ └── seo-redirects.conf.j2 # SEO redirect examples - └── generic/ - ├── wordpress-permalinks.conf.j2 # Common WordPress redirects - └── ssl-redirects.conf.j2 # HTTP to HTTPS redirects -``` - -## How Nginx Includes Work in Trellis - -Trellis supports nginx-includes templates that are deployed to the server during provisioning. These templates are stored in your project's `trellis/nginx-includes/` directory. - -### Include Scopes - -1. **Global includes** (`nginx-includes/all/`): Apply to all sites -2. **Site-specific includes** (`nginx-includes/{site-key}/`): Apply to specific sites only - -### Directory Structure in Your Project - -``` -trellis/ -└── nginx-includes/ - ├── all/ # Global includes (all sites) - │ └── assets-expiry.conf.j2 # Browser caching rules - └── example.com/ # Site-specific includes - └── seo-redirects.conf.j2 # SEO redirects (example.com only) -``` - -### Deployment Location - -Templates are deployed to `/etc/nginx/includes.d/` on the server: - -``` -/etc/nginx/includes.d/ -├── all/ -│ └── assets-expiry.conf # Rendered from all/assets-expiry.conf.j2 -└── example.com/ - └── seo-redirects.conf # Rendered from example.com/seo-redirects.conf.j2 -``` - -## Using the Examples - -### 1. Copy Example to Your Project - -```bash -# Copy site-specific redirect template -cp ~/code/trellis-tools/redirects/examples/example.com/seo-redirects.conf.j2 \ - ~/code/yourproject.com/trellis/nginx-includes/yoursite.com/ - -# Or copy to global includes (applies to all sites) -cp ~/code/trellis-tools/redirects/examples/generic/wordpress-permalinks.conf.j2 \ - ~/code/yourproject.com/trellis/nginx-includes/all/ -``` - -### 2. Edit the Template - -Edit the `.conf.j2` file to add your site-specific redirects: - -```nginx -# SEO Redirects - Fix 404 Errors -# Date: 2025-11-30 - -# Old URL structure -> New URL structure -location = /old-page/ { - return 301 /new-page/; -} - -# Old category -> New category -location = /category/old-category/ { - return 301 /category/new-category/; -} -``` - -### 3. Deploy to Production - -```bash -cd trellis - -# Deploy nginx-includes only (fast) -trellis provision --tags nginx-includes production - -# Or deploy with full wordpress-setup -trellis provision --tags wordpress-setup production -``` - -Do not use `--tags nginx`. The includes are templated by the `wordpress-setup` -role, so that run finishes without deploying the file. - -### 4. Verify Deployment - -```bash -# Check the config file was deployed -ssh web@yoursite.com "ls -la --time-style=full-iso /etc/nginx/includes.d/yoursite.com/" -ssh web@yoursite.com "grep -n 'old-url' /etc/nginx/includes.d/yoursite.com/seo-redirects.conf" - -# Test nginx config is valid -ssh -t admin_user@yoursite.com "sudo nginx -t" - -# Test a redirect -curl -I https://yoursite.com/old-url -# Should show: HTTP/2 301 -# Location: https://yoursite.com/new-url/ -``` - -## Redirect Best Practices - -### 1. Use Permanent Redirects (301) - -```nginx -# ✅ Good - Permanent redirect (SEO-friendly) -location = /old-page/ { - return 301 /new-page/; -} - -# ⚠️ Temporary redirect (use sparingly) -location = /maintenance/ { - return 302 /temporary-page/; -} -``` - -### 2. Match Exact Paths with `=` - -```nginx -# ✅ Fast - Exact match (stops processing) -location = /old-page/ { - return 301 /new-page/; -} - -# ⚠️ Slower - Prefix match (continues processing) -location /old-page/ { - return 301 /new-page/; -} -``` - -### 3. Use Regex for Pattern Matching - -```nginx -# Redirect all portfolio items to main portfolio page -location ~ ^/portfolio-item/.*$ { - return 301 /portfolio/; -} - -# Redirect old date-based URLs to new structure -location ~ ^/(\d{4})/(\d{2})/(.*)$ { - return 301 /$3; -} -``` - -### 4. Query Strings Are Not Preserved by `return` - -```nginx -# The query string is dropped -location = /old-page/ { - return 301 /new-page/; -} -# /old-page/?utm_source=google -> /new-page/ - -# Append it to keep it -location = /old-page/ { - return 301 /new-page/$is_args$args; -} -# /old-page/?utm_source=google -> /new-page/?utm_source=google -``` - -`return` does not carry the query string over (`rewrite` does). Keep it for -redirects people reach from campaigns; drop it when the destination is another -site and the old parameters mean nothing there. - -### 5. Add Comments and Dates - -```nginx -# SEO Redirects - Fix 404 Errors -# Date: 2025-11-30 -# Reason: Content restructure after site migration - -location = /old-page/ { - return 301 /new-page/; -} -``` - - -## Testing Redirects - -### Manual Testing with curl - -```bash -# Test redirect (follow redirects with -L) -curl -I https://yoursite.com/old-url - -# Expected output: -# HTTP/2 301 -# Location: https://yoursite.com/new-url/ -``` - -### Automated Testing - -Create a test script to verify all redirects: - -```bash -#!/bin/bash -# test-redirects.sh - -SITE="https://yoursite.com" - -# Test each redirect -curl -I -s "$SITE/old-page/" | grep -q "301" && echo "✅ /old-page/ -> 301" || echo "❌ /old-page/ failed" -curl -I -s "$SITE/another-page/" | grep -q "301" && echo "✅ /another-page/ -> 301" || echo "❌ /another-page/ failed" -``` - - -## Finding URLs to redirect - -Start from your 404s: Google Search Console's coverage report, or `wp-ops 404-checker`, or the nginx access log. Compare the old and new sitemaps for removed URLs. - -## Rollback - -If redirects cause issues, you can remove them: - -```bash -# 1. Remove or comment out redirects in the .conf.j2 file - -# 2. Re-deploy -cd trellis -trellis provision --tags nginx-includes production - -# 3. Or manually remove on server -ssh -t admin_user@yoursite.com "sudo rm /etc/nginx/includes.d/yoursite.com/seo-redirects.conf && sudo nginx -t && sudo systemctl reload nginx" -``` - - -## Troubleshooting - -### Redirects Not Working - -```bash -# 1. Verify file was deployed -ssh web@yoursite.com "ls -la --time-style=full-iso /etc/nginx/includes.d/yoursite.com/" - -# 2. Check nginx config syntax -ssh -t admin_user@yoursite.com "sudo nginx -t" - -# 3. Check nginx error log -ssh -t admin_user@yoursite.com "sudo tail -f /var/log/nginx/error.log" - -# 4. Verify nginx was reloaded -ssh -t admin_user@yoursite.com "sudo systemctl status nginx" -``` - -### 404 Instead of Redirect - -- Check if redirect location path is exact match (`=`) -- Verify trailing slashes (nginx treats `/page` and `/page/` differently) -- Check if another location block takes precedence - -### Redirect Loops - -```bash -# Test for loops -curl -I -L https://yoursite.com/page/ - -# If it hangs or shows many redirects, you have a loop -``` - -Fix by checking your redirect chain: -```nginx -# ❌ Bad - Creates loop -location = /page-a/ { return 301 /page-b/; } -location = /page-b/ { return 301 /page-a/; } - -# ✅ Good - Direct to final destination -location = /page-a/ { return 301 /final-page/; } -location = /page-b/ { return 301 /final-page/; } -``` - -## Documentation - -- [Roots Trellis Nginx Includes Docs](https://roots.io/trellis/docs/nginx-includes/) -- [Nginx Location Directive](http://nginx.org/en/docs/http/ngx_http_core_module.html#location) -- [Nginx Return Directive](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#return) diff --git a/docs/nginx/redirects/examples/generic/ssl-redirects.conf.j2 b/docs/nginx/redirects/examples/generic/ssl-redirects.conf.j2 index 1df8b78..aab3807 100644 --- a/docs/nginx/redirects/examples/generic/ssl-redirects.conf.j2 +++ b/docs/nginx/redirects/examples/generic/ssl-redirects.conf.j2 @@ -1,3 +1,4 @@ +# Guide: https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/ # SSL/HTTPS Redirects # Force HTTPS for specific paths (if not using global HTTPS redirect) diff --git a/docs/nginx/redirects/examples/generic/wordpress-permalinks.conf.j2 b/docs/nginx/redirects/examples/generic/wordpress-permalinks.conf.j2 index 78852bb..8e9d584 100644 --- a/docs/nginx/redirects/examples/generic/wordpress-permalinks.conf.j2 +++ b/docs/nginx/redirects/examples/generic/wordpress-permalinks.conf.j2 @@ -1,3 +1,4 @@ +# Guide: https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/ # WordPress Permalink Redirects # Common WordPress URL structure migrations diff --git a/docs/nginx/redirects/examples/imagewize.com/seo-redirects.conf.j2 b/docs/nginx/redirects/examples/imagewize.com/seo-redirects.conf.j2 index 869bffb..e36e009 100644 --- a/docs/nginx/redirects/examples/imagewize.com/seo-redirects.conf.j2 +++ b/docs/nginx/redirects/examples/imagewize.com/seo-redirects.conf.j2 @@ -1,3 +1,4 @@ +# Guide: https://wpvilla.in/trellis-nginx-redirect-not-deployed-query-string-dropped/ # SEO Redirects - Fix 404 Errors # Date: 2025-11-30 # Trellis Child Template - deployed via 'trellis provision --tags nginx-includes production' From 0237fc805a1178d6558e2009a7ee0e2dab5c3011 Mon Sep 17 00:00:00 2001 From: Jasper Frumau Date: Wed, 28 Oct 2026 13:41:21 +0700 Subject: [PATCH 2/2] Fix the deny-list hint in security-monitor: includes are folders, not a nginx_includes setting --- scripts/monitoring/security-monitor.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/monitoring/security-monitor.sh b/scripts/monitoring/security-monitor.sh index f0cd9ed..95f7922 100755 --- a/scripts/monitoring/security-monitor.sh +++ b/scripts/monitoring/security-monitor.sh @@ -507,10 +507,10 @@ generate_block_recommendations() { echo "" echo -e "${CYAN}To block in Trellis:${NC}" - echo "1. Create roles/wordpress-setup/templates/deny-ips.conf.j2" + echo "1. Create trellis/nginx-includes/all/deny-ips.conf.j2" echo "2. Add the deny statements above" - echo "3. Add to wordpress_sites YAML: nginx_includes: deny-ips.conf" - echo "4. Run: trellis provision production" + echo "3. Run: trellis provision --tags nginx-includes production" + echo " Guide: https://wpvilla.in/nginx-deny-list-trellis-cidr-whitelist-self-lockout/" } # ============================================================================