<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jay Sullivan</title>
    <description>The latest articles on DEV Community by Jay Sullivan (@paidhosting).</description>
    <link>https://dev.to/paidhosting</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4152486%2F70bcd07d-f159-42b2-952e-e12edfe75f45.png</url>
      <title>DEV Community: Jay Sullivan</title>
      <link>https://dev.to/paidhosting</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paidhosting"/>
    <language>en</language>
    <item>
      <title>The same redirect and bot-blocking rules for Apache, Nginx, Caddy and Cloudflare</title>
      <dc:creator>Jay Sullivan</dc:creator>
      <pubDate>Thu, 15 Oct 2026 10:20:49 +0000</pubDate>
      <link>https://dev.to/paidhosting/the-same-redirect-and-bot-blocking-rules-for-apache-nginx-caddy-and-cloudflare-3bae</link>
      <guid>https://dev.to/paidhosting/the-same-redirect-and-bot-blocking-rules-for-apache-nginx-caddy-and-cloudflare-3bae</guid>
      <description>&lt;p&gt;Most server rules you find online are written for one server. Copy an &lt;code&gt;.htaccess&lt;/code&gt; snippet to Nginx and nothing happens, because Nginx never reads &lt;code&gt;.htaccess&lt;/code&gt;. Write HTTPS and www as two separate rules and every visitor takes two redirects instead of one.&lt;/p&gt;

&lt;p&gt;Here are two everyday jobs written for all four places they usually live, plus the one mistake that turns a redirect list into a slow mess.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Force HTTPS and drop www, in one redirect
&lt;/h2&gt;

&lt;p&gt;The goal: &lt;code&gt;http://www.example.com/page&lt;/code&gt; goes straight to &lt;code&gt;https://example.com/page&lt;/code&gt; in a single hop, not two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apache / LiteSpeed (.htaccess)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{HTTPS} &lt;span class="ss"&gt;off&lt;/span&gt; [OR]
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^www\. [NC]
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ https://%1%{REQUEST_URI} [L,R=301]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third condition captures the domain without &lt;code&gt;www.&lt;/code&gt;, so one rule fixes both problems at once. Behind Cloudflare or another proxy, &lt;code&gt;%{HTTPS}&lt;/code&gt; can read &lt;code&gt;off&lt;/code&gt; even when the visitor is on HTTPS; use &lt;code&gt;RewriteCond %{HTTP:X-Forwarded-Proto} !https [OR]&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nginx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://example.com&lt;/span&gt;&lt;span class="nv"&gt;$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need a second &lt;code&gt;server&lt;/code&gt; block for &lt;code&gt;https://www.example.com&lt;/code&gt; that returns the same redirect, with your certificate on it. No &lt;code&gt;if&lt;/code&gt;, no rewrite: a separate server block is the fastest and clearest way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caddy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;www.example.com, http://www.example.com, http://example.com {
    redir https://example.com{uri} permanent
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caddy redirects HTTP to HTTPS on its own, so you only list the addresses that should land on the main one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No rule needed: turn on &lt;strong&gt;SSL/TLS &amp;gt; Edge Certificates &amp;gt; Always Use HTTPS&lt;/strong&gt;, and create a redirect rule from the &lt;strong&gt;Redirect from WWW to root&lt;/strong&gt; template under &lt;strong&gt;Rules&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Block AI training crawlers
&lt;/h2&gt;

&lt;p&gt;The crawlers that collect training data identify themselves in the User-Agent. (List shortened here; the full, current list is in the generator.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apache / LiteSpeed&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
&lt;span class="nc"&gt;RewriteCond&lt;/span&gt; %{HTTP_USER_AGENT} (GPTBot|ClaudeBot|CCBot|Bytespider) [NC]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^ - [F,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nginx&lt;/strong&gt; (the &lt;code&gt;map&lt;/code&gt; goes once inside &lt;code&gt;http {}&lt;/code&gt;, the &lt;code&gt;if&lt;/code&gt; inside your &lt;code&gt;server {}&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="nv"&gt;$http_user_agent&lt;/span&gt; &lt;span class="nv"&gt;$blocked_ua&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;"~*&lt;/span&gt;&lt;span class="s"&gt;(GPTBot|ClaudeBot|CCBot|Bytespider)"&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$blocked_ua&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Caddy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com {
    @ai_train header_regexp User-Agent "(?i)(GPTBot|ClaudeBot|CCBot|Bytespider)"
    respond @ai_train 403
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt; (Security &amp;gt; Security rules &amp;gt; Create rule &amp;gt; Edit expression, action: Block)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(lower(http.user_agent) contains "gptbot")
or (lower(http.user_agent) contains "claudebot")
or (lower(http.user_agent) contains "ccbot")
or (lower(http.user_agent) contains "bytespider")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the same names to &lt;code&gt;robots.txt&lt;/code&gt; as well: well-behaved crawlers stop there, and the server rule catches the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The redirect-list mistake: chains
&lt;/h2&gt;

&lt;p&gt;After a few redesigns, a redirect list ends up like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/old-page   /new-page
/2019/sale  /sale
/sale       /offers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A visitor to &lt;code&gt;/2019/sale&lt;/code&gt; now takes two hops: to &lt;code&gt;/sale&lt;/code&gt;, then to &lt;code&gt;/offers&lt;/code&gt;. Search engines follow chains, but each hop is wasted time, and long chains get abandoned. Fix the list so every old URL points at its final address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/2019/sale  /offers
/sale       /offers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worse is a loop (&lt;code&gt;/a → /b&lt;/code&gt;, &lt;code&gt;/b → /a&lt;/code&gt;), which redirects forever until the browser gives up. Check for both every time you add lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  A generator that does all of this
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://www.paidhosting.com/" rel="noopener noreferrer"&gt;PaidHosting.com&lt;/a&gt;, and we built a free generator that writes these rules, and more, for all four servers from the same switches: &lt;a href="https://www.paidhosting.com/htaccess-nginx-generator/" rel="noopener noreferrer"&gt;.htaccess, Nginx &amp;amp; Caddy Rule Generator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It shortens redirect chains and removes loops for you, blocks AI crawlers by group (training vs search), handles domain moves, removing &lt;code&gt;.php&lt;/code&gt; from URLs and password-protected folders, and gives you a share link to your exact setup. We tested the output on Apache, Nginx, Caddy, LiteSpeed and a real Cloudflare account before release.&lt;/p&gt;

&lt;p&gt;The code is open source (MIT): &lt;a href="https://github.com/PaidHost/server-rules-generator" rel="noopener noreferrer"&gt;https://github.com/PaidHost/server-rules-generator&lt;/a&gt;. Bug reports for your own server setup are very welcome.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nginx</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
