# H1 Keyword Highlighting – Production Fix

**Last Updated:** 2026-02-11

## Issue Summary

H1 keyword highlighting worked locally but not on production. Blog post titles appeared without highlighted keywords (Ordio blue) on ordio.com.

## Root Cause

Production environments may lack the `mbstring` PHP extension. Unconditional calls to mb_ functions caused fatal errors, which were caught by PostHeader and triggered a fallback to plain `htmlspecialchars($title)` – so the title rendered without highlighting.

**Problematic calls (before fix):**

1. **Lines 2442–2448** – UTF-8 validation block using `mb_check_encoding()`, `mb_convert_encoding()`, and `mb_detect_encoding()` without checking whether mbstring is loaded.
2. **Line 2521** – `mb_strtolower()` used directly without `_has_mbstring()` check.

**Why it worked locally:** macOS and most dev setups include mbstring by default. Production (ordio.com) may use a different PHP build or hosting where mbstring is optional or disabled.

## Fixes Applied

### 1. UTF-8 validation block (lines 2440–2459)

- Wrapped in `function_exists('mb_check_encoding') && function_exists('mb_convert_encoding')`.
- If mbstring is available: keep existing mb_check_encoding / mb_convert_encoding / mb_detect_encoding logic.
- If not: use `iconv('UTF-8', 'UTF-8//IGNORE', $title)` when iconv is available.
- If neither: assume UTF-8 from JSON source and continue.

### 2. mb_strtolower call (line 2521)

- Replaced with:  
  `_has_mbstring() ? mb_strtolower($before_separator, 'UTF-8') : _mb_strtolower_fallback($before_separator, 'UTF-8')`

### 3. _mb_strlen_fallback improvement (line 2243)

- Replaced deprecated `utf8_decode()` with `preg_match_all('/./u', $str, $m)` for UTF-8-safe character count.
- Avoids PHP 8.2 deprecation and invalid UTF-8 issues with utf8_decode.

## Verification

### Local testing

```bash
# Standard tests
php v2/scripts/blog/test-title-highlighting.php

# Simulate production without mbstring
php v2/scripts/blog/test-title-highlighting.php --simulate-no-mbstring
```

### Production verification

1. Deploy changes.
2. Open sample blog posts:
   - `/insights/inside-ordio/david-keuenhof-im-kuechenherde-podcast/`
   - `/insights/lexikon/krankmeldung/`
3. Confirm keywords are highlighted in Ordio blue.
4. Check error logs for any `highlight_title_keywords` errors.

### Extension check

```bash
php v2/scripts/dev-helpers/check-php-extensions.php
```

If mbstring is missing: either enable it on production (e.g. `apt install php-mbstring`) or rely on the fallbacks (highlighting works without mbstring).

## Files Modified

| File | Change |
|------|--------|
| `v2/config/blog-template-helpers.php` | Guard UTF-8 validation, fix mb_strtolower, improve _mb_strlen_fallback |
| `v2/scripts/blog/test-title-highlighting.php` | Add `--simulate-no-mbstring` flag |
| `docs/content/blog/TITLE_KEYWORD_HIGHLIGHTING.md` | Add production troubleshooting |
| `docs/content/blog/H1_HIGHLIGHTING_PRODUCTION_FIX.md` | This document |
| `.cursor/rules/blog-production.mdc` | Pre-deploy extension checklist |

## Related Documentation

- [TITLE_KEYWORD_HIGHLIGHTING.md](./TITLE_KEYWORD_HIGHLIGHTING.md) – Full highlighting guide
- [.cursor/rules/php-extensions.mdc](../../.cursor/rules/php-extensions.mdc) – PHP extension patterns
- [docs/development/PHP_EXTENSION_DEPENDENCIES.md](../development/PHP_EXTENSION_DEPENDENCIES.md) – Extension dependencies
