Embedding Module7 in WordPress
This guide covers the full setup for embedding Module7 in WordPress using the JS embed approach.
With the JS embed, Module7 content is rendered in the browser after the page loads. Search engine crawlers that do not execute JavaScript will not index the content.
If SEO is a requirement, a server-side adapter is needed instead.
1. Server Configuration
The web server must proxy /module7/* requests to the Module7 server and route deep-linked view URLs back to WordPress.
The production Module7 server address is https://mo7.vns.services.
Nginx
Add the following to your .conf file (e.g. m7proxy.conf):
# Required to allow sub_filter to work on compressed responses
map $scheme $fcgi_https { https on; default ''; }
# Inject the Module7 loader into every HTML response
sub_filter '</head>' '<script src="/module7/loader.js"></script></head>';
sub_filter_once on;
# Route /listing and /listing/* to WordPress's index.php.
# Catches both plain /listing and path-based filter URLs like /listing/type=poi.
location ~ ^/listing {
fastcgi_pass unix:/run/php-fpm.sock;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param REQUEST_URI /listing;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fcgi_https;
fastcgi_read_timeout 60s;
}
# Route /detail/* to WordPress's index.php.
# WordPress issues canonical 301/302 redirects for unknown URLs — the
# @detail_serve fallback block re-serves the same FastCGI response
# as a 200 without following the redirect.
location ~ ^/detail/.+ {
fastcgi_pass unix:/run/php-fpm.sock;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param REQUEST_URI /detail/;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fcgi_https;
fastcgi_read_timeout 60s;
fastcgi_intercept_errors on;
error_page 301 302 =200 @detail_serve;
}
location @detail_serve {
fastcgi_pass unix:/run/php-fpm.sock;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param REQUEST_URI /detail/;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fcgi_https;
fastcgi_read_timeout 60s;
}
# Proxy /module7/* to the Module7 server
location ^~ /module7/ {
proxy_pass https://mo7.vns.services/module7/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Accept-Encoding "";
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Integration-Key" always;
}
Apache
Add the following to your .htaccess file at the root of your WordPress installation (the same folder as wp-config.php).
Your .htaccess already contains a # BEGIN WordPress / # END WordPress block managed by WordPress. Paste the Module7 block above that block — WordPress regenerates everything between those markers and will overwrite anything placed inside them.
### Begin: Module7 ###
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Route /detail/* to the WordPress /detail page
RewriteCond %{THE_REQUEST} \s/detail/[^\s]+
RewriteRule ^detail/.*$ index.php?pagename=detail [L]
# Route /listing and /listing?... to the WordPress /listing page
RewriteCond %{REQUEST_URI} ^/listing
RewriteRule ^listing index.php?pagename=listing [L]
</IfModule>
<IfModule mod_substitute.c>
<IfModule mod_filter.c>
AddOutputFilterByType SUBSTITUTE text/html
Substitute "s|</head>|<script src='/module7/loader.js'></script></head>|i"
</IfModule>
</IfModule>
### End: Module7 ###
# BEGIN WordPress ← your existing block starts here, do not touch it
Your final .htaccess structure must look like this:
### Begin: Module7 ### ← added by you, above the WordPress block
...
### End: Module7 ###
# BEGIN WordPress ← existing block, do not touch
...
# END WordPress
The reverse proxy rule cannot go into .htaccess — it must be added to your Apache virtual host configuration by your hosting provider or server administrator:
On shared hosting, contact your hosting provider and ask them to add a reverse proxy rule for /module7/ pointing to https://mo7.vns.services.
# Add inside your <VirtualHost> block (both *:80 and *:443)
ProxyPreserveHost On
ProxyPass /module7/ https://mo7.vns.services/module7/
ProxyPassReverse /module7/ https://mo7.vns.services/module7/
<Location /module7/>
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type, Integration-Key"
</Location>
2. Loading the loader script
If your web server is configured to inject the loader script automatically (via sub_filter or mod_substitute as shown above), no further action is needed.
If you prefer to include it via WordPress instead, add the following to your theme's functions.php:
add_action('wp_head', function () {
echo '<script src="/module7/loader.js"></script>';
});
Both approaches are equivalent.
3. Required WordPress pages
For deep-linked URLs to work, you must create two pages in WordPress with the following exact slugs:
| Page | Slug | Purpose |
|---|---|---|
| Listing | /listing | Handles listing view URLs, e.g. /listing?city=Heidelberg&type=venus%3AHikingTrail |
| Detail | /detail | Handles detail view URLs, e.g. /detail/id=68480f7917d87aa853d49bea |
Without these pages, deep-linked URLs will return a 404. If you don't need deep-link support and always use the view attribute on <m7-view>, this step can be skipped.
To create the pages:
- Go to Pages → Add New in the WordPress admin
- Set the title (e.g. "Listing") and make sure the slug matches exactly (
/listing) - Add the
<m7-view>element to the page content (see next section) - Publish the page
- Repeat for
/detail
4. Placing <m7-view> on a page
Once the loader is active, add a <m7-view> element to any WordPress page where Module7 content should appear.
In the block editor (Gutenberg), use a Custom HTML block:
<m7-view
view="listing"
token="your-integration-key">
</m7-view>
Fallback for editors that sanitize HTML
Some WordPress configurations, themes, or page builders remove unknown HTML tags like <m7-view> when saving content. If you see an empty <div> in the saved output instead of your element, use the data-m7-view fallback on a plain <div>:
<div
data-m7-view
data-view="listing"
data-token="your-integration-key">
</div>
The loader automatically upgrades these <div> placeholders to proper <m7-view> elements. All attributes work identically with the data- prefix.
URL-driven usage (for /listing and /detail pages)
On the pages you created in step 3, omit the view attribute. Module7 resolves the view from the current URL automatically:
<m7-view token="your-integration-key"></m7-view>
This way a single element handles all listing states (/listing, /listing?city=Berlin, /listing?time=next_week) and all detail views (/detail/id=abc123) without any changes to the page.
5. Available attributes
For a full reference of all <m7-view> attributes including filter, project, channel, and mode, see the <m7-view> Attribute Reference.