Appearance
Apache routing for dynamic WeWeb pages (advanced)
This page is an advanced guide for users who:
- Are self‑hosting a WeWeb app behind Apache
- Want page that have dynamic path parameters such as
/product/{{product_id}}/detailsto avoid 404s
What this guide covers
- How a typical Vue.js / WeWeb build is structured
- How to write
.htaccessrules so Apache serves a specific template file (for example,/product/:param/details/index.html) when a user visits/product/5/details - How this can help search engines and social networks access those URLs without 404s
Understanding the build structure
When you export your WeWeb / Vue.js app and run npm run build, your dist folder might look like:
txt
dist/
├── index.html # Homepage (root)
├── assets/ # JavaScript, CSS, fonts
├── data/ # JSON data files
├── products/
│ └── index.html # Products listing page
└── product/
└── :param/
└── details/
└── index.html # Product details page templateKey idea:
:paramis a literal folder name produced by the build./product/:param/details/index.htmlis a template file used for all product details pages.
You might have many real URLs:
/product/1/details/product/5/details/product/99/details
But there is only one template file on disk:
/product/:param/details/index.html
The problem without custom routing
When a user directly visits /product/5/details:
- Apache looks for
/product/5/details/index.html - That file does not exist
- Result:
404 Not Found
Even if your Vue app would handle /product/5/details correctly once loaded, the first request never reaches it because Apache returns a 404.
The Apache .htaccess configuration
Place the following in a .htaccess file in your web server’s document root (where your built index.html lives):
apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Don't rewrite direct index.html requests
RewriteRule ^index\.html$ - [L]
# Map dynamic product detail URLs to the static :param template
# Example: /product/5/details -> /product/:param/details/index.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^product/([^/]+)/details/?$ /product/:param/details/index.html [L]
# Map dynamic profile URLs to the static :param template
# Example: /profile/5 -> /profile/:param/index.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^profile/([^/]+)/?$ /profile/:param/index.html [L]
# Fallback: serve root index.html for any other non-existent paths
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>How it works
Enable rewriting
apache<IfModule mod_rewrite.c> RewriteEngine On RewriteBase /Checks that
mod_rewriteis enabled and turns on URL rewriting.Preserve direct
index.htmlrequestsapacheRewriteRule ^index\.html$ - [L]If someone directly requests
/index.html, Apache serves it as is.Handle parameterized URLs for products
apacheRewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^product/([^/]+)/details/?$ /product/:param/details/index.html [L]- Only runs when there is no physical file (
! -f) and no directory (! -d). - Matches URLs like
/product/5/detailsor/product/123/details/. - Rewrites them to
/product/:param/details/index.html, which is the template file from the build.
- Only runs when there is no physical file (
Handle parameterized URLs for profiles
apacheRewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^profile/([^/]+)/?$ /profile/:param/index.html [L]- Matches URLs like
/profile/5or/profile/5/. - Rewrites them to
/profile/:param/index.html.
- Matches URLs like
Fallback rule
apacheRewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]Any other non‑existing path falls back to
/index.html, which lets Vue Router handle normal SPA routes.
Real‑world example
Product detail page with parameter
Request (local testing):http://localhost:8080/product/5/details
What Apache does:
- Checks: Does
/product/5/detailsexist as a file? → No - Checks: Does
/product/5/detailsexist as a directory? → No - Matches rule
^product/([^/]+)/details/?$→ Yes (captures5) - Serves
/product/:param/details/index.html
Example HTML served (simplified):
html
<!DOCTYPE html>
<html>
<head>
<title>Detailed page for one product</title>
<meta name="description" content="Description for product detailed page" />
<meta property="og:title" content="Detailed page for one product" />
</head>
<body>
<div id="app"></div>
<script src="/assets/main-Bdry9Nph.js"></script>
</body>
</html>The exact
<title>and<meta>tags depend on how you configured page metadata inside WeWeb.
Use Dynamic page metadata to control those values.
Static directory page
Request: http://localhost:8080/products
- Apache sees that
/productsis a real directory. - It serves
/products/index.htmldirectly. - No rewrite rule is triggered.
Static asset
Request: http://localhost:8080/assets/main.js
- Apache sees
assets/main.jsis a real file. - It serves the file directly.
- No rewriting occurs.
Impact on SEO and social sharing
Routing alone does not create SEO‑friendly content. It simply ensures:
- Search engines and social crawlers reach your dynamic URLs without hitting 404s.
- The HTML they see on first load comes from the right template file.
Quick setup checklist
Enable
mod_rewriteMake sure
mod_rewriteis loaded in your Apache config, for example:bashgrep "rewrite_module" /etc/apache2/httpd.confAllow
.htaccessoverridesIn your Apache virtual host configuration, ensure
.htaccessis allowed to define rewrite rules:apache<Directory "/Library/WebServer/Documents"> AllowOverride All </Directory>Build and deploy
bashnpm run build ./deploy-app.sh # or your own deployment scriptRestart Apache
bashsudo apachectl restartVerify one dynamic URL
bashcurl http://localhost:8080/product/5/details | grep "<title>"You should see the
<title>coming from the correct template HTML file.
Troubleshooting
404 errors on parameterized URLs?
- Check that
mod_rewriteis enabled. - Verify that
.htaccessis in the correct document root. - Ensure
AllowOverride All(or at leastAllowOverride FileInfo) is set for that directory.
Changes not taking effect?
bash
sudo apachectl configtest
sudo apachectl restart
