All websites on our shared hosting platform are served by our own HTTP server. This article explains how your site is served and which special functions and configuration options are available to you.
Most options are configured in a file called .htconfig.yaml in the root folder of your website. Don’t hesitate to contact us if you want any of these options changed — we’re happy to help.
How your files are served
The server matches the requested domain name to a folder on the server. For example, requests for www.example.com are served from that domain’s own folder. Multiple domain names can point to the same website. If no folder matches, a default site is used when one is configured.
When a .htconfig.yaml file exists in your site’s root folder, it controls the behaviour described below.
Choosing a preset
The preset key sets the default behaviour of your website:
wordpressdrupal7shopware6craftcms- SPA (single-page application)
static
For wordpress, drupal7, shopware6 and craftcms, index.php is served when there is no immediate file match — this is what makes “pretty permalinks” work.
The wordpress preset has extra security optimisations:
xmlrpc.phpis not served (error 401) unless you explicitly enable it withxmlrpc: true.- PHP execution is blocked inside
wp-content/uploadsandwp-content/cache— the directories where plugins (and attackers) can write files. A PHP webshell uploaded there can never run.
If a legitimate plugin needs PHP execution inside one of those directories, or you want to block additional directories, this can be fine-tuned per site with allow/deny lists (allow rules always win):
php:
exec_paths:
allow: ["wp-content/cache/my-plugin"] # carve-out: execution allowed here again
deny: ["wp-content/uploads/private"] # extra blocked directory
This only affects direct web requests to PHP files; PHP’s own internal file includes keep working everywhere.
For a single-page application, the SPA preset serves index.html as the fallback for unknown paths, so client-side routing works. The fallback file can be changed, e.g. for an SPA bootstrapped by PHP:
preset: SPA spa: fallback: index.php
The static preset is meant for plain static sites (for example Astro or Hugo output). Requests resolve in this order: an exact file match, then index.html inside the requested directory, then a custom 404.html in the site root if you created one. Sensible caching headers are added automatically: HTML pages are always revalidated so your updates are visible immediately, content-hashed assets (such as Astro’s /_astro/ files) are cached for a year, and other files (images, fonts, …) for one hour.
Redirects
You can define redirects in .htconfig.yaml, matched by regular expression, path prefix, or exact path. The first matching rule (from top to bottom) wins:
redirects: - regex: ^/old-blog/(.*)$ target: /blog/$1 - prefix: /shop target: https://shop.example.com - fullpath: /old-page target: /new-page
Choosing your PHP version
Your site can be pinned to a specific PHP version:
php: version: 8.3
Available versions are mapped server-side; let us know if you want to switch.
Restoring the visitor’s real IP address
Because all traffic passes through our proxy, PHP’s $_SERVER["REMOTE_ADDR"] would normally show the proxy’s address instead of your visitor’s. Setting the following in .htconfig.yaml restores the real address:
remote_address: restore_to_forwarded_for
Image optimization
Resizing images via the URL
Images can be resized on the fly by adding a special prefix to the request path. For example:
https://www.example.com/!resize:cover:540x400/uploads/photo.jpg
This serves uploads/photo.jpg resized to 540×400 pixels. Available strategies:
cover: keeps the aspect ratio and fills the whole space; excess pixels are cropped (centered).fit: keeps the aspect ratio and fits within the space; empty space is filled with black.
Modern formats (WebP, AVIF)
You can also request a modern image format directly via the URL:
/!format:webp/uploads/photo.jpg— serve as WebP (default quality 82)/!format:webp:75/uploads/photo.jpg— WebP at quality 75/!format:avif/uploads/photo.jpg— AVIF (default quality 60)/!original/uploads/photo.jpg— force the untouched original
These compose with resizing, e.g. /!resize:cover:540x400/!format:webp/uploads/photo.jpg.
Automatic image optimization
On request, we can enable automatic optimization per site. Two complementary modes exist, both opt-in:
- HTML rewriting: your pages’
<img>tags are automatically rewritten to offer WebP/AVIF variants (via a<picture>element or a src swap based on the browser’s capabilities). Images are converted once and cached; your visitors automatically get the smallest file their browser supports. - Direct-request override: plain requests for
.jpg/.pngfiles are answered with the optimized WebP/AVIF variant directly.
Converted images are cached on the server, so the conversion cost is paid only once per image. Contact us if you want this enabled for your site.
Page cache
For WordPress sites we can enable a full-page cache: pages for anonymous visitors are served directly from the server’s memory instead of running PHP on every request. This makes your site noticeably faster and reduces load — especially during traffic spikes.
- Only anonymous traffic is cached. As soon as a visitor is logged in, has something in their cart, or has commented (detected via cookies), their requests bypass the cache and are rendered fresh.
- Admin and API paths (
/wp-admin/,/wp-login.php,/wp-json/, …) are never cached. - You can see the cache in action via the
X-Cacheresponse header:HITmeans served from memory,MISSmeans freshly rendered by PHP. - Cached pages expire automatically. By default a page stays cached for 60 seconds (plus a small random margin, so many pages don’t all expire at the same moment). A hard upper limit of 10 minutes guarantees no visitor ever sees a page that is more than 10 minutes stale. Both can be tuned per site via
default_ttl_secondsandmax_ttl_seconds. The cache can also be purged automatically when you publish or update content.
The page cache is opt-in per site — contact us to have it enabled.
Behind the memory cache there is a second layer that serves WP Rocket’s own on-disk pages directly, so PHP is skipped even when a page is not held in memory. Cache effectiveness is measured per site: see Cache and scheduled-task metrics for what we record, how to read the hit rate, and which “misses” are deliberate.
Rate limiting
To protect your site against brute-force attacks and abusive bots, we can apply rate limits to specific paths — for example limiting requests to /wp-login.php or your API endpoints. Visitors who exceed the limit are first delayed, and only receive an error (HTTP 429, “too many requests”) if they persist. Normal visitors never notice this.
A limit can be counted in one of two ways, and the difference matters:
- Per visitor (the default) — every IP address gets its own allowance. This is what you want for login protection: one attacker is slowed down without affecting anyone else.
- Per client type — one shared allowance for everyone matching a given browser identification (User-Agent). This is what you want for crawlers, because a large crawler arrives from hundreds of different IP addresses at once; a per-visitor limit would never trigger, since each individual address looks perfectly reasonable.
Managing crawlers and AI bots
Search engines and, increasingly, AI crawlers (ClaudeBot, GPTBot, PerplexityBot, Amazonbot and others) can generate a large share of a site’s total traffic. Unlike a search engine, an AI crawler usually sends no visitors back to you, so it is worth keeping its appetite in proportion.
We manage this on two levels:
- A polite request — your
robots.txtasks these crawlers to slow down and to skip pages that cost a lot to generate but are worthless to index, such as internal search results and shop filter URLs. Well-behaved crawlers honour this. Your existing SEO plugin keeps full control ofrobots.txt; these directives are added alongside whatever it already publishes, never replacing it. - An enforced limit — a shared per-crawler ceiling applied at our edge, so a crawler that ignores
robots.txtstill cannot monopolise the server. A full crawl of your site still completes; it simply takes hours rather than minutes.
Individual crawlers can be slowed further, or blocked outright, per site. Requests a person is actively waiting on — such as an AI assistant fetching one page on a user’s behalf — are deliberately left unthrottled, as are Google and Bing, which do send visitors back to you.
Hard blocks
Beyond rate limiting, specific requests can be refused outright based on the path, the browser identification, or a combination — returning either “not found” (404) or “forbidden” (403). We use this for known attack tooling and for endpoints that have no legitimate public use on your site. Because these blocks are applied before PHP starts, they cost your site essentially nothing, even under a heavy attack.
PHP worker limits
Each site gets its own pool of PHP worker processes, and that pool has two settings we can tune per site: how many workers a site may occupy at once, and how long an idle worker is kept alive before being retired. Raising the worker count suits a busy site with expensive pages; lowering it protects the rest of the server from a single site absorbing everything during a traffic spike. The defaults are appropriate for a normal WordPress site, so this is only adjusted when there is a reason to.
If your site is generating a lot of PHP work, the first thing worth checking is not these limits but the page cache — a cached page never starts PHP at all.
Contact forms without PHP
Static sites can have fully working contact forms handled by the server itself — no PHP or external form service needed. Your HTML form simply posts to /_form/{name} on your own domain. Submissions are validated, stored, and e-mailed to you.
Features:
- Field validation (required fields, maximum lengths, e-mail format).
- Spam protection via a hidden “honeypot” field and optional Cloudflare Turnstile or hCaptcha.
- Per-visitor submission rate limiting.
- Custom e-mail subject and body templates, and custom success/error pages to redirect to.
Ask us to set up a form for your site.
Pre-compressed static assets
If your build process generates pre-compressed files (for example style.css.br or style.css.gz next to style.css), the server serves them directly to browsers that support Brotli or gzip — with the correct content type. This gives you maximum compression with zero runtime cost.
Further things to note
- GET requests to
wp-login.phpare cached in server memory as an optimisation. - Behind the scenes the server also protects all sites with automatic abuse detection: clients that misbehave (aggressive scraping, attacking common vulnerability paths) are rate-limited or blocked before they ever reach your site.
See also: