Skip to content
Back to skills

Craftcms Ops

ASecurity

Craft CMS 3/4/5 agency site builds: Matrix-as-entries content modeling, Twig and element queries, eager loading, output escaping and CSRF, and the usual plugin stack - SEOmatic, Blitz, Formie, CKEditor, craft-vite - plus DDEV, Codeception, image transforms, the queue, and 3-to-4-to-5 upgrades. Use when building, debugging, securing, speeding up, or upgrading a Craft site, or editing its Twig templates, config/project, or any of those plugins.

  • 50 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
ai-agentsgophpbashsqlvuedebugginggitapifrontendsecurity

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned October 6, 2026

npx -y skills add 0xDarkMatter/claude-mods --skill craftcms-ops --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Craftcms Ops?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Craftcms Ops
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/0xdarkmatter-craftcms-ops/badge)](https://www.skillsdirectory.com/skills/0xdarkmatter-craftcms-ops)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: craftcms-ops
description: "Craft CMS 3/4/5 agency site builds: Matrix-as-entries content modeling, Twig and element queries, eager loading, output escaping and CSRF, and the usual plugin stack - SEOmatic, Blitz, Formie, CKEditor, craft-vite - plus DDEV, Codeception, image transforms, the queue, and 3-to-4-to-5 upgrades. Use when building, debugging, securing, speeding up, or upgrading a Craft site, or editing its Twig templates, config/project, or any of those plugins."
license: MIT
allowed-tools: "Read Write Bash"
metadata:
  author: claude-mods
  related-skills: "ddev-ops, laravel-ops, sql-ops, nginx-ops, perf-ops, web-perf-ops, frontend-upgrade-ops, tailwind-ops, playwright-ops, a11y-ops, security-ops"
---

# Craft CMS Operations

> Versions verified 2026-10-05 against Packagist and the official docs. Craft 5.x is
> current (5.11); Craft 4 and 3 are past security support; Craft 6 is in alpha.
> `scripts/check-craft-facts.py --live` re-checks the plugin majors on a schedule.

The recurring agency shape: Craft 5 (or a 4/3 site awaiting upgrade) on DDEV, with
SEOmatic, Blitz, Formie, and CKEditor, a Vite (or legacy Laravel Mix) front end, and
modules tested with Codeception and ECS. This file is the procedure and the router;
each topic lives in one reference.

## Step 1 - Orient before touching anything

| Check | Where | Tells you |
|-------|-------|-----------|
| Craft + plugin versions | `composer.json`, `ddev composer show craftcms/cms` | Which line of the [version matrix](#version-matrix) applies - Craft 4 and 5 APIs differ |
| Local environment | `.ddev/config.yaml` | PHP/DB versions; run everything as `ddev craft`, `ddev composer`, `ddev npm` |
| Schema | `config/project/*.yaml` | Source of truth for sections, fields, entry types, plugin settings |
| Config | `config/general.php`, `config/<plugin>.php`, `.env` | Environment-specific behaviour (`devMode`, `allowAdminChanges`, caching) |
| Front-end build | `package.json` + `config/vite.php`, or `webpack.mix.js` | craft-vite or Laravel Mix |
| Templates | `templates/_layouts`, `_partials`, `_partials/entry/<type>.twig` | Layout inheritance and element partials |
| PHP | `modules/`, `tests/`, `codeception.yml`, `ecs.php` | Where logic and tests live |
| Page cache | `config/blitz.php`, Blitz utility | Whether what you see is cached HTML - check before debugging "my change isn't showing" |

## Step 2 - Route the task

| Task | Read |
|------|------|
| Listing pages, relations, Matrix/nested entries, pagination, multi-site | [element-queries.md](references/element-queries.md) |
| Printing anything user-influenced, forms that POST, JS data, rich text output | [twig-security.md](references/twig-security.md) |
| Meta tags, JSON-LD, sitemaps, robots.txt, hreflang | [seomatic.md](references/seomatic.md) |
| Static caching, "changes don't show", cache warming, dynamic bits on cached pages | [blitz.md](references/blitz.md) |
| Building, theming, or debugging forms; spam; form emails not sending | [formie.md](references/formie.md) |
| Rich-text fields, nested entries inside rich text, Redactor conversion | [ckeditor.md](references/ckeditor.md) |
| Craft on DDEV: the `craftcms` type, `ddev craft`, Craft's `upload_dirs` (generic DDEV: `ddev-ops`) | [ddev.md](references/ddev.md) |
| Tests for modules/plugins, fixtures, ECS/PHPStan | [codeception.md](references/codeception.md) |
| Front-end assets, Vite dev server, critical CSS | [craft-vite.md](references/craft-vite.md) |
| Moving off Laravel Mix/Webpack, or Vue 2 to Vue 3 | `frontend-upgrade-ops` (the full migration playbook) |
| Upgrading Craft 3 → 4 → 5, plugin version lines, Craft 6 status | [upgrades.md](references/upgrades.md) |
| Slow pages, images, queue setup, production config | [performance.md](references/performance.md) |
| Core Web Vitals failing (LCP, INP, CLS), PageSpeed or Search Console flags | `web-perf-ops` (field data first; its `references/craft.md` maps the Craft levers) |
| Headless front ends, GraphQL schemas and tokens | [graphql.md](references/graphql.md) |
| Modules, plugins, events, migrations, queue jobs | [plugin-development.md](references/plugin-development.md) |
| Modeling a new flexible page type | [assets/entry-type-field-layout.md](assets/entry-type-field-layout.md) |

## Step 3 - Hold the non-negotiables

1. **Eager-load before you loop.** `.with([...])` up front; `.eagerly()` inside shared
   partials (Craft 5 only). One query per card is the most common Craft perf bug.
2. **Escape by context.** Never `|raw` user-influenced values; `|e('js')` inside JS
   strings; `{{ csrfInput() }}` in every POST form - `csrfInput({ async: true })` on
   cached pages.
3. **Schema only through Project Config.** Change it in the CP locally, commit
   `config/project/`, run `php craft up` on deploy. Production runs
   `allowAdminChanges => false`; never hand-edit the YAML.
4. **Data changes are content migrations** (`php craft migrate/create`), tested on a
   copy of production - not CP clicking on live.
5. **Production runs a queue worker.** Blitz regeneration, Formie emails and
   integrations, transform pre-generation, and resaves are all queue jobs.
6. **Fix queries, then cache.** Blitz for public pages; on Blitz-cached pages drop
   `{% cache %}` (or set `enableTemplateCaching` false).
7. **Logic lives in module services**, not Twig - services are testable.
8. **Stay on the latest Craft 5 patch.** Craft ships regular Twig/RCE security fixes.

## Version matrix

| Package | Craft 3 | Craft 4 | Craft 5 (current) |
|---------|---------|---------|-------------------|
| `craftcms/cms` | 3.9 (EOL) | 4.18 (EOL Apr 2026) | Craft 5.x (5.11); Craft 6 in alpha |
| `nystudio107/craft-seomatic` | 3.x | 4.x | SEOmatic 5 |
| `putyourlightson/craft-blitz` | 3.x | 4.x | Blitz 5 (5.13 needs Craft 5.6+) |
| `verbb/formie` | 1.x | 2.x | Formie 3 (Formie 4 in beta) |
| Rich text | Redactor | Redactor or `craftcms/ckeditor` 3.x | CKEditor plugin 5.x (needs Craft 5.10+; 4.x below that) |
| `nystudio107/craft-vite` | 1.x | 4.x | craft-vite 5 |
| `nystudio107/craft-imageoptimize` | 1.x | 4.x | ImageOptimize 5 |
| `spacecatninja/imager-x` | 3.x | 4.x | Imager-X 6 (5.x still maintained) |
| `codeception/codeception` | match core's `require-dev` | match core's `require-dev` | Codeception 5 |

Requirements for Craft 5: PHP 8.2+, MySQL 8.0.17+ / MariaDB 10.4.6+ / PostgreSQL 13+.
Details and plugin upgrade notes: [upgrades.md](references/upgrades.md).

## Everyday commands

| Command (prefix `ddev` locally) | Does |
|---------------------------------|------|
| `craft up` | Pending migrations + Project Config - run on every deploy |
| `craft project-config/apply` / `project-config/rebuild` | Apply YAML to the DB / rebuild YAML from the DB |
| `craft clear-caches/all` | Data, template, and asset caches |
| `craft queue/run` / `queue/info` / `queue/retry all` | Work and inspect the queue |
| `craft blitz/cache/refresh` | Refresh Blitz after template deploys (Blitz tracks content, not templates) |
| `craft migrate/create <name>` | New content migration |
| `craft make <type>` | Scaffold modules/plugins/components (`craftcms/generator`) |
| `craft entrify/categories <group>` | Convert categories (or `tags`, `global-set`) to entries |

## Craft 5 content model

| Concept | What it is | Craft 5 change |
|---------|-----------|----------------|
| **Section** | Single, Channel, or Structure; holds entry types + URI formats | - |
| **Entry type** | The unit of content shape | **Global and reusable** across sections and Matrix fields |
| **Field** | Reusable input | **Global**, with multi-instance use in one layout |
| **Matrix field** | Repeatable nested content | Stores **nested entries**, not blocks |
| **CKEditor field** | Rich text | Can hold **nested entries** inline |
| **Element partials** | `_partials/entry/<typeHandle>.twig` | `.render()` renders nested entries through them |
| **Project Config** | `config/project/` YAML | Source of truth - commit it |

Pick the section type by shape: **Single** for one-off pages (home, contact),
**Channel** for streams (news, events), **Structure** for hierarchies (pages, docs).
Prefer flat entry types plus a Matrix "page builder" field over many near-identical
sections. Starter shape: [entry-type-field-layout.md](assets/entry-type-field-layout.md).

## Gotchas

| Symptom | Cause | Fix |
|---------|-------|-----|
| Listing page runs hundreds of queries | Relation fetched per item | `.with()` / `.eagerly()` |
| Change visible in CP, not on site | Blitz/CDN serving cached HTML | [blitz.md](references/blitz.md#debugging-my-change-isnt-showing) |
| Form submissions save, emails never send | Queue not running in production | Queue daemon ([performance.md](references/performance.md#queue)) |
| Assets unstyled in production only | Vite 5+ manifest is in `dist/.vite/` | Set `manifestPath` ([craft-vite.md](references/craft-vite.md)) |
| `{% set seomatic.meta.seoTitle = ... %}` does nothing | `set` only reads | `{% do seomatic.meta.seoTitle('...') %}` |
| Staging copy of the site got indexed / prod de-indexed | SEOmatic environment wrong | Check robots in view-source ([seomatic.md](references/seomatic.md#environments)) |
| Project Config conflicts between developers | CP edits on shared/prod environments | `allowAdminChanges` false outside local; one schema change per PR |
| Event handler fires several times per save | Drafts, revisions, propagation | Guard with `ElementHelper::isDraftOrRevision()` + `propagating` |
| `craft.matrixBlocks()` errors after upgrade | Removed in Craft 5 | `craft.entries().field(...).owner(...)` |

## Bundled resources

| File | Use |
|------|-----|
| `assets/entry-type-field-layout.md` | Content-modeling starter: section + entry type + Matrix-as-entries, mapped to Project Config |
| `assets/craft-facts.json` | The version facts this skill documents (package, major, prose token) |
| `scripts/check-craft-facts.py` | Staleness verifier: `--offline` (prose still states the facts) / `--live` (Packagist majors) |

## See also

- `ddev-ops` (generic DDEV: version pinning, env files, snapshots and sanitised pulls,
  Mutagen, add-ons, Xdebug, and an auditor for `.ddev/` mistakes)
- `laravel-ops` (Composer/PHP tooling; Craft 6 is Laravel-based) · `sql-ops` (indexes behind
  slow `orderBy`) · `nginx-ops` (serving Craft, Blitz rewrites) · `perf-ops` (profiling) ·
  `tailwind-ops` · `playwright-ops` (browser tests against the DDEV URL)
- `web-perf-ops` (Core Web Vitals: CrUX/RUM first, then the failing subpart; Blitz, transforms,
  craft-vite critical CSS and SEOmatic/Formie script placement as INP/LCP levers)
- `frontend-upgrade-ops` (Laravel Mix or Webpack to Vite via craft-vite, Vue 2 to Vue 3,
  Vue islands in Twig, and when a widget should become Alpine instead)
- `a11y-ops` (WCAG 2.2 for Twig sites: heading levels across partials, asset alt text, Formie
  and CKEditor markup, multi-site `lang`; see its `references/server-rendered-templates.md`)
- `security-ops` (Craft/Twig/PHP security: CSRF and Formie, `allowAnonymous`, devMode and the
  security key, GraphQL scoping, uploads, advisories and Craft 3/4 end of life; its
  `references/craft-*.md`, `twig-*.md`, `php-*.md`)
- [Craft 5 docs](https://craftcms.com/docs/5.x/) · [Plugin Store](https://plugins.craftcms.com/) ·
  [Craft security advisories](https://github.com/craftcms/cms/security/advisories)

**Why this shape:** a 2026 survey of 57 Craft-agency repositories found Craft in 36
(Craft 5 ×17, 4 ×13, 3 ×6), DDEV in 36, SEOmatic in 33, Blitz 21, Formie 18,
CKEditor 18, Laravel Mix 18 versus craft-vite 12, Tailwind 12, ECS 11, and Codeception
10. The references follow that frequency; upgrades matter because half the Craft sites
were on an EOL major.

Files in this skill

  • SKILL.md9 KB
  • assets/entry-type-field-layout.md2.6 KB
  • references/graphql-and-plugins.md3.5 KB
  • references/twig-and-queries.md4.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…