# Server Rendering

Orderly storefronts can server-render product and category pages for SEO while keeping the browser web components in charge of layout and interaction.

The server emits semantic fallback HTML inside the page component. Googlebot and View Source see real headings, product links, images, prices, descriptions, and schema.org Product/Offer metadata. The HTML source is the same for users and crawlers. Normal browser requests get a tiny inline CSS block that hides the fallback and route component light DOM until the web components hydrate; Googlebot user agents get a visible default fallback layout. When JavaScript runs, the web components read the markup as initial state, hide the fallback, and continue as normal client components.

Static prehydrated pages cannot branch on `User-Agent`, so they keep the visible fallback layout. Dynamic dev SSR and generated PHP renderers choose visibility per request.

SSR is only the entry-render path when the package storefront router is enabled. After first load, same-origin category, product, checkout, payment-result, and configured static-page anchors are intercepted and rendered through `pushState`, so the app-shell and global component state stay alive and no second SSR layout pass is shown.

## Local Dev

Scaffolded shops use the Vite middleware by default:

```js
import { orderlySsrDevServer } from "@orderlyshop/web-components/server/vite";

export default defineConfig({
  plugins: [htmlIncludes(), orderlySsrDevServer(), rootHtmlOutput()]
});
```

Run the shop and inspect View Source:

```sh
npm run dev
open http://localhost:61677/categories/sko/
```

`ORDERLY_DEV_SSR=0 npm run dev` or `npm run dev -- --no-ssr` disables SSR for debugging pure client rendering. Dev SSR reads `VITE_ORDERLY_BASE_URL`, `ORDERLY_BASE_URL`, or falls back to `https://service.orderly.shop`. It uses `grpc-web` and a short in-memory cache; set `ORDERLY_DEV_SSR_CACHE_TTL_MS=0` to disable that cache.

## Apache + PHP

Generate production renderers after the static build:

```sh
npm run build
npx orderly-generate-server-renderers --site-title "My Shop"
```

The command writes:

- `dist/product.php`
- `dist/category.php`
- `dist/.htaccess`
- `dist/orderly-ssr-manifest.json`

Deploy `dist` to Apache with `AllowOverride FileInfo` or equivalent enabled. Existing files and assets are served normally. Product paths under `/products/` are rewritten to `product.php`; category paths under `/categories/` are rewritten to `category.php`.

The generated `.htaccess` also sets long immutable `Cache-Control` headers for fingerprinted static assets such as JS, CSS, images, SVG, and fonts, while keeping HTML/PHP revalidatable. If the hosting platform ignores `.htaccess` (for example some static CDN or cloud storage setups), configure the same cache policy in the host: hashed assets under `dist/assets/` should be cached for a year with `immutable`, while HTML entrypoints should not.

PHP runtime configuration:

- `ORDERLY_BASE_URL` overrides the backend URL.
- `ORDERLY_SSR_TIMEOUT` controls backend timeout in seconds.
- `ORDERLY_SSR_HEADERS` appends server-side request headers.
- `ORDERLY_SSR_MANIFEST` points PHP at a manifest outside the web root.

Keep credentials server-side. Do not expose API keys, OAuth tokens, refresh tokens, or cookie values to browser JavaScript.

## SSR Markup Contract

Product cards and product detail fallbacks should be normal HTML first:

```html
<article data-orderly-product itemscope itemtype="https://schema.org/Product"
  data-orderly-share-url="https://orderly.shop/example.html"
  data-orderly-sku-value="SKU-1"
  data-orderly-price-amount="100"
  data-orderly-price-currency="DKK">
  <link itemprop="url" href="https://orderly.shop/example.html">
  <link itemprop="image" href="https://cdn.example/product.jpg">
  <meta itemprop="sku" content="SKU-1">
  <h1 data-orderly-field="title" itemprop="name">Example product</h1>
  <p data-orderly-field="brand" itemprop="brand">Example brand</p>
  <p data-orderly-field="price" itemprop="offers" itemscope itemtype="https://schema.org/Offer">
    <meta itemprop="priceCurrency" content="DKK">
    <meta itemprop="price" content="100.00">
    kr. 100,00
  </p>
</article>
```

The `data-orderly-*` attributes are only for behavior-critical values that are not reliable to infer from visible text. Components still work without SSR markup and will fetch data in the browser as before.

Do not remove SSR fallback markup as a FOUC workaround. Keep the semantic HTML in the response and let the package critical SSR CSS choose visibility per request. No-JS clients receive a `<noscript>` fallback style, and Googlebot user agents receive the visible fallback layout directly.

## Legacy Helpers

The older copyable `server/php/orderly-product.php`, `server/apache/.htaccess`, `server/nginx/orderly-products.conf`, and `server/node/product-snapshot-server.mjs` files remain in the package for existing deployments. New shops should prefer `orderly-generate-server-renderers`.
