Why Official Mintlify Reverse Proxy Docs Might Fail You (and How We Fixed It)
0. The Root Problem: The Subfolder Hosting Gap
The journey didn’t start with a proxy; it started with a limitation. Mintlify is a fantastic documentation platform, but it has a specific constraint: you can only map custom domains to subdomains (e.g., docs.yourdomain.com). Most modern marketing and SEO strategies, however, demand that documentation live in a subfolder (e.g., yourdomain.com/docs).
Because Mintlify doesn’t natively support subfolder hosting, you are forced to set up a Reverse Proxy to “bridge” the gap. This seemingly simple requirement is where the technical nightmare begins, especially when enterprise-grade CDNs like CloudFront are in the mix.
1. The Challenge: The CloudFront-Vercel Security Wall
The biggest hurdle we faced wasn’t the proxy setup itself, but the security layers between our infrastructure and Mintlify’s hosting provider, Vercel.
The Real-IP Problem
When a request travels from a user → CloudFront → Your Server (Apache) → Vercel (Mintlify), the original user’s IP address often gets lost or replaced by the server’s internal/static IP.
Why httpd.conf Failed with CloudFront
The official docs suggest using ProxyPass and ProxyPassReverse in your Apache httpd.conf. While this works for simple setups, it failed us for two reasons:
- CloudFront Header Stripping: CloudFront distributions often strip or modify headers before they reach your Apache server. If Apache then tries to proxy that request to Vercel without the correct
X-Forwarded-Forheaders, Vercel’s security layer flags the request as suspicious. - Vercel Security Errors: Because Vercel serves Mintlify, it has strict protections. Requests coming from fixed/static IPs of a proxy server (rather than the “Real IP” of the user) frequently trigger 403 Forbidden errors or Vercel’s automated bot protection.
2. The Alternative: Standard httpd.conf (Non-CloudFront Setups)
If you are not using CloudFront and have a direct connection from the internet to your Apache server, you can likely use the standard httpd.conf approach. This is cleaner as it handles proxying at the server level.
Below is the httpd.conf configuration we initially used. It includes specific exclusions for WordPress paths to ensure your blog remains untouched while the documentation is proxied.
<!-- httpd.conf -->
# Reverse Proxy Configuration
SSLProxyEngine on
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
# Handle static assets first
ProxyPass /mintlify-assets/ https://koreai.mintlify.app/mintlify-assets/
ProxyPassReverse /mintlify-assets/ https://koreai.mintlify.app/mintlify-assets/
# Main documentation paths
ProxyPass /agent-platform/ https://koreai.mintlify.app/agent-platform/
ProxyPassReverse /agent-platform/ https://koreai.mintlify.app/agent-platform/
# WORDPRESS EXCLUSIONS (Do not proxy these)
ProxyPass /wp-admin !
ProxyPass /wp-includes !
ProxyPass /wp-content !
ProxyPass /index.php !
# Root domain to Mintlify Home
ProxyPass / https://koreai.mintlify.app/home/
ProxyPassReverse / https://koreai.mintlify.app/home/
# ----------------------------------------------------
# CRITICAL: Header and Location Rules
# These rules ensure Vercel/Mintlify receives the correct
# IP and Host information to prevent 403 errors.
# ----------------------------------------------------
<Location "/agent-platform">
RequestHeader unset Host
RequestHeader unset Accept-Encoding
RequestHeader set Origin "https://koreai.mintlify.app"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-Proto %{REQUEST_SCHEME}s
RequestHeader set User-Agent "%{User-Agent}i"
Header set Cache-Control "no-cache, no-store, must-revalidate"
</Location>
<Location "/_mintlify">
RequestHeader unset Host
RequestHeader set Origin "https://koreai.mintlify.app"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
RequestHeader set User-Agent "%{User-Agent}i"
Header set Cache-Control "no-cache, no-store, must-revalidate"
</Location>
<Location "/mintlify-assets/_next/static">
RequestHeader unset Host
RequestHeader set Origin "https://koreai.mintlify.app"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
RequestHeader set User-Agent "%{User-Agent}i"
# Enable caching for static assets
Header set Cache-Control "public, max-age=86400"
</Location>
# (Repeat similar <Location> blocks for /ai-for-work, /ai-for-service, etc.)
3. The Working Solution: The PHP Proxy “Bridge”
To overcome the CloudFront IP masking, we moved away from the static httpd.conf approach and implemented a dynamic PHP-based Proxy Bridge. This gave us granular control over every header and allowed us to rewrite content on the fly.
Step 1: .htaccess Routing
First, we used .htaccess to capture documentation-specific paths and route them to our proxy script.
# .htaccess
RewriteEngine On
RewriteBase /
# Route root and specific paths to the custom proxy.php
RewriteRule ^$ proxy.php [QSA,L]
RewriteRule ^llms\.txt($|.*) proxy.php [QSA,L]
RewriteRule ^sitemap\.xml($|.*) proxy.php [QSA,L]
RewriteRule ^agent-platform($|[\./].*) proxy.php [QSA,L]
RewriteRule ^mintlify-assets($|[\./].*) proxy.php [QSA,L]
# Leave standard WordPress rules below...
Step 2: The proxy.php Script
This script handles the cURL request, manually reconstructs the “Real IP” headers, and rewrites URLs in the response to maintain SEO consistency.
<?php
// proxy.php
$targetBaseUrl = 'https://koreai.mintlify.app';
$requestUri = $_SERVER['REQUEST_URI'];
if ($requestUri == '/' || $requestUri == '') { $requestUri = '/home/'; }
$url = $targetBaseUrl . $requestUri;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
// Manual Header Reconstruction for Vercel/CloudFront Compatibility
$headers = [];
$headers[] = "X-Forwarded-For: " . $_SERVER['REMOTE_ADDR'];
$headers[] = "X-Real-IP: " . $_SERVER['REMOTE_ADDR'];
$headers[] = "Origin: https://koreai.mintlify.app";
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$responseBody = substr($response, $headerSize);
curl_close($ch);
// Rewrite absolute links to match the front-facing domain
$responseBody = str_ireplace('https://koreai.mintlify.app', 'https://docs.kore.ai', $responseBody);
echo $responseBody;
?>
Conclusion
The official documentation is a great starting point, but it’s not a silver bullet for enterprise-grade deployments involving CDNs like CloudFront. Notably, Mintlify’s guides focus on standard server-level configurations but fail to provide alternatives using scripting languages like PHP or Perl.
In complex environments where network-level headers are stripped or security layers are overly aggressive, a script-based proxy bridge is often the only way to bypass these restrictions. By choosing this approach, we gained the flexibility to satisfy Vercel’s security requirements while maintaining absolute control over our documentation’s SEO and branding.
By following this approach, we successfully hosted our docs at docs.kore.ai while keeping the content managed on Mintlify’s platform.