How to Make example.com/blog Serve Content From blog.example.com on IIS (Without Redirect)

Many clients host their PHP application on a subdomain like blog.example.com, but later decide that they want the same site to appear under a subdirectory such as:

https://example.com/blog

And they want:

  • NO redirect
  • URL must stay example.com/blog
  • Content must come from blog.example.com
  • All internal links must continue to work

This can only be done using a Reverse Proxy.

On an IIS server, the correct way to do this is using:

  • Application Request Routing (ARR)
  • URL Rewrite

This combination allows IIS to forward requests internally to the subdomain and rewrite outbound links so the user never sees the subdomain.


✔ Why Point 4 (Inbound Rewrite Only) Is NOT Enough

Many developers try only the following inbound rule:

<rule name="Blog Reverse Proxy" stopProcessing="true">
  <match url="^blog/(.*)" />
  <action type="Rewrite" url="http://blog.example.com/{R:1}" />
</rule>

This will load the PHP site from the subdomain, but it causes major problems:

  • CSS, JS, and images will still load from blog.example.com
  • Internal links redirect to blog.example.com
  • Forms submit to the subdomain
  • Login redirects break (WordPress, Laravel, etc.)
  • The illusion of /blog is lost

This happens because the backend continues to generate absolute URLs.

So inbound only = proxy works, but site breaks.


✔ What You Actually Need

To make the site fully functional under example.com/blog, you need two rules:


1. Inbound Rewrite (Reverse Proxy Request Forwarding)

This forwards all /blog/... requests to the PHP site.

<rule name="Blog Reverse Proxy" stopProcessing="true">
  <match url="^blog/(.*)" />
  <action type="Rewrite" url="http://blog.example.com/{R:1}" />
  <serverVariables>
    <set name="HTTP_X_Forwarded_For" value="{REMOTE_ADDR}" />
    <set name="HTTP_X_Forwarded_Proto" value="https" />
    <set name="HTTP_HOST" value="blog.example.com" />
  </serverVariables>
</rule>

2. Outbound Rewrite (Fix HTML Links, Images, CSS, JS)

This replaces URLs inside HTML output so browsers never see the subdomain.

<outboundRules>
  <rule name="Fix absolute URLs" preCondition="IsHTML">
    <match filterByTags="A, Form, Img, Script, Link"
           pattern="http://blog\.example\.com/(.*)" />
    <action type="Rewrite" value="/blog/{R:1}" />
  </rule>

  <preConditions>
    <preCondition name="IsHTML">
      <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
    </preCondition>
  </preConditions>
</outboundRules>

Now all links such as:

http://blog.example.com/category
http://blog.example.com/wp-content/themes/style.css

automatically become:

/blog/category
/blog/wp-content/themes/style.css

This keeps users inside /blog forever.


🧩 Final Combined Working web.config

Put this inside the main site (example.com) web.config:

<configuration>
  <system.webServer>
    <rewrite>

      <!-- Inbound reverse proxy rule -->
      <rules>
        <rule name="Blog Reverse Proxy" stopProcessing="true">
          <match url="^blog/(.*)" />
          <action type="Rewrite" url="http://blog.example.com/{R:1}" />
          <serverVariables>
            <set name="HTTP_X_Forwarded_For" value="{REMOTE_ADDR}" />
            <set name="HTTP_X_Forwarded_Proto" value="https" />
            <set name="HTTP_HOST" value="blog.example.com" />
          </serverVariables>
        </rule>
      </rules>

      <!-- Outbound rules to fix internal links -->
      <outboundRules>
        <rule name="Fix absolute URLs" preCondition="IsHTML">
          <match filterByTags="A, Form, Img, Script, Link"
                 pattern="http://blog\.example\.com/(.*)" />
          <action type="Rewrite" value="/blog/{R:1}" />
        </rule>

        <preConditions>
          <preCondition name="IsHTML">
            <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
          </preCondition>
        </preConditions>
      </outboundRules>

    </rewrite>
  </system.webServer>
</configuration>

🎉 Result

After applying both rules:

✔ Visiting example.com/blog will load the PHP site
✔ URL will NOT redirect
✔ All CSS, JS, and images will load correctly
✔ Internal site links stay under /blog
✔ Backend (PHP) keeps running normally on blog.example.com

This is the correct and complete reverse proxy setup for IIS.