Skip to main content Spring til indhold

Sådan fungerer WordPress: arkitektur og performance

Når nogen åbner en side på din WordPress-side, sker der en hel del, før browseren får den første byte: serveren starter PHP, WordPress indlæser kernen, alle aktive plugins og temaet, spørger databasen og bygger HTML’en. Hvert led koster tid, og ofte er det ét eller to af dem, der gør en side langsom. Har du en langsom WooCommerce-shop og vil finde årsagen, så følg fejlfindingen trin for trin.

Her følger vi én request hele vejen igennem og peger på, hvor tiden typisk går. Hvor et emne fortjener mere plads, linker vi videre til den guide, der går i dybden.

Fra klik til færdig side

Sådan ser rejsen ud for en almindelig sidevisning, der ikke rammer en cache:

  1. Browseren slår domænet op og sender en HTTP-request til serveren. Med HTTP/3 kan forbindelsen komme hurtigere op, fordi der skal færre rundture til.
  2. Webserveren, typisk Nginx eller Apache, kan ikke selv lave siden og sender requesten videre til PHP.
  3. PHP kører WordPress, som indlæser konfiguration, kerne, plugins og tema.
  4. WordPress læser URL’en og finder ud af, hvilket indhold der er bedt om.
  5. Hovedforespørgslen og en række mindre forespørgsler henter indholdet i databasen.
  6. Temaet vælger en template og bygger HTML’en, mens plugins hænger deres egne ting på undervejs.
  7. Den færdige HTML sendes tilbage, og browseren begynder at hente billeder, CSS og JavaScript.

Trin 2 til 6 er det meste af serverens svartid, den tid der går, før browseren får den første byte (TTFB). Trin 7 er browserens arbejde. Resten af guiden går trinnene igennem i samme rækkefølge.

Webserveren og PHP

Webserveren er hurtig til det, den selv kan: filer som billeder og stylesheets sender den direkte. Alt, der skal genereres, går videre til PHP, som regel gennem PHP-FPM, der holder et antal PHP-processer (workers) klar. Hver worker håndterer én request ad gangen. Er de alle optaget, står de næste i kø, og det er her, en side, der føles hurtig med én besøgende, bliver langsom under en kampagne.

To ting på serveren sætter bunden for, hvor hurtig WordPress kan blive. Den ene er PHP-versionen, fordi nyere versioner typisk kører den samme kode hurtigere. Den anden er OPcache, der gemmer PHP-filerne i kompileret form, så hundredvis af filer fra kerne og plugins ikke skal fortolkes forfra på hver request. Begge dele afhænger af valget af hosting, og i guiden om PHP-optimering i WordPress går vi i dybden med versioner, OPcache og workers.

Bootstrap: det WordPress indlæser på hver request

Når PHP starter WordPress, går det altid samme vej. index.php kalder wp-blog-header.php, der gennem wp-load.php læser wp-config.php. Derefter sætter wp-settings.php resten i gang: kernens funktioner, dine must-use plugins, alle aktive plugins, temaets functions.php og til sidst hooket init, hvor de fleste plugins gør sig klar.

Det vigtige er, at det sker på hver eneste request, der ikke rammer en cache. Et plugin, du kun bruger på kontaktsiden, bliver stadig indlæst, når nogen åbner en produktside, og hvis det laver databaseopslag eller kalder et eksternt API allerede ved init, betaler hver besøgende for det. Derfor siger antallet af plugins ikke så meget i sig selv; det er det, de gør, når de indlæses, der tæller. Vi har skrevet om et konkret eksempel: et plugin, der brugte 100 ms på JSON på kategorisiderne i en webshop, fordi en gammel funktion behandlede teksten tegn for tegn.

Mappestrukturen følger samme logik. wp-includes er kernen, wp-admin er administrationen, som kun indlæses, når nogen er i kontrolpanelet, og wp-content er dit: tema, plugins og uploads. Det er typisk i wp-content, du skal lede efter performanceproblemer, fordi det er der, tredjepartskoden ligger.

wp-config.php rummer mere end adgangen til databasen. Her sætter du blandt andet, hvor meget hukommelse WordPress må bruge:

define( 'WP_MEMORY_LIMIT', '256M' );     // forsiden
define( 'WP_MAX_MEMORY_LIMIT', '512M' ); // administrationen

Grænsen kan ikke komme over det, serveren tillader, og en højere grænse gør ikke siden hurtigere. Den forhindrer bare, at tunge sider fejler.

Hooks: hvor plugins hænger sig på

Plugins og temaer ændrer ikke WordPress’ kerne. De hænger sig på hooks, faste punkter i koden, hvor WordPress spørger, om nogen vil gøre noget. En action er et tidspunkt, fx wp_head, når sidens head bygges, eller save_post, når et indlæg gemmes. Et filter er en værdi, der sendes forbi alle, der har meldt sig, og kan ændres undervejs:

add_filter( 'the_content', function ( $content ) {
    return $content . '<p>Tak fordi du læste med.</p>';
} );

Det gør WordPress fleksibelt, men det gør også omkostningen usynlig. the_content kan have ti callbacks fra fem forskellige plugins, og de kører alle, hver gang indholdet vises. Et filter, der laver en databaseforespørgsel, bliver kaldt for hvert produkt i en liste. Vil du se, hvad der faktisk hænger på en side, viser Query Monitor både hooks og de plugins, der står bag dem.

Databasen: hvor tiden ofte går

Når WordPress har læst URL’en, bygger det hovedforespørgslen med WP_Query: den, der henter indlægget, siden eller listen af produkter, som URL’en peger på. Omkring den kommer en række mindre forespørgsler fra kernen, temaet og plugins. På en simpel blogside er det få. På en webshop med filtre og mange plugins kan det løbe op i hundreder.

Datamodellen forklarer meget. Alt indhold, fra indlæg til menupunkter, ligger i wp_posts, og alle ekstra felter ligger i wp_postmeta som par af nøgle og værdi. Det er fleksibelt, men værdierne er ikke indekseret, så et filter på værdien af et felt kan ende med at læse hele tabellen. Det er den slags forespørgsler, der gør produktfiltre langsomme i store butikker. WooCommerce har af samme grund flyttet ordrer ud af wp_posts og ind i deres egne tabeller (High-Performance Order Storage), som er standard i nye butikker.

Den tredje tabel at kende er wp_options. Alle indstillinger, der er markeret til automatisk indlæsning, hentes samlet på hver request, så plugins, der gemmer store mængder data der, gør hver side lidt tungere. Du kan tjekke størrelsen sådan (tabelpræfikset er ikke altid wp_):

SELECT ROUND(SUM(LENGTH(option_value)) / 1024) AS kb
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto');

Ligger tallet på flere megabyte, er det værd at finde ud af, hvilke plugins der står bag. Hvordan du finder og retter de tunge forespørgsler, gennemgår vi i guiden til en hurtigere WordPress-database.

Template og output

Når data er hentet, vælger WordPress en template efter template-hierarkiet: den mest specifikke, der findes for den slags indhold, fx single-product.php før single.php før index.php. Med bloktemaer og sidebyggere som Oxygen er princippet det samme, men skabelonerne ligger i databasen i stedet for i filer. Undervejs registrerer tema og plugins deres stylesheets og scripts, og WordPress skriver dem ind i sidens head og bund. Det er her, en side får mange ekstra filer med, hvis hvert plugin indlæser sine filer overalt. Hvad browseren så gør med dem, handler guiden om billeder, CSS og JavaScript om.

Caching: de lag, der springer arbejde over

Caching betyder, at noget arbejde gøres én gang og genbruges. Lagene sparer hver sin del af rejsen:

LagGemmerSparerBegrænsning
SidecacheDen færdige HTMLPHP, WordPress og databasen heltVirker ikke på sider, der er forskellige for hver besøgende: kurv, checkout, min konto
Object cacheResultater af forespørgsler og beregningerGentagne databaseopslagHusker kun mellem requests med Redis eller Memcached
TransientsMidlertidige data med udløbstid, fx svar fra et APIEksterne kald og tunge beregningerLigger i databasen, hvis der ikke er en object cache
OPcacheKompileret PHP-kodeFortolkning af PHP-filerneSlås til på serveren, ikke i WordPress
Browser og CDNBilleder, CSS og JavaScriptAt hente filerne igenHjælper ikke på HTML’en for dynamiske sider

For en blog er sidecachen næsten det hele: de fleste besøgende får en færdig HTML-fil, og PHP kører slet ikke. En webshop er anderledes. Kurv og checkout kan ikke sidecaches, og det samme gælder resten af siden, når kunden har lagt noget i kurven, så netop de requests, der tjener penge, går hele vejen gennem WordPress. Her gør object cachen forskellen, fordi den tager presset af databasen på hver af dem. Hvilket plugin, der forbinder WordPress med Redis, betyder også noget; se vores benchmark af object cache-plugins.

Admin-ajax, REST API og WP-Cron

Ikke alle requests er sidevisninger. admin-ajax.php er WordPress’ gamle vej til dynamiske kald fra browseren, og hvert kald starter hele WordPress inklusive administrationsdelen. Heartbeat-API’et bruger den til at sende et kald med faste mellemrum, så længe nogen har kontrolpanelet åbent. REST API’et er den nyere vej: det starter også WordPress, men indlæser ikke administrationen, og svarene kan caches.

WP-Cron er ikke en rigtig cron. Planlagte opgaver startes, når nogen besøger siden, så på en side med lidt trafik kører de for sent, og på en travl side tjekkes der hele tiden. Det er bedre at slå det fra og lade serveren køre det fast:

// wp-config.php
define( 'DISABLE_WP_CRON', true );

# crontab på serveren, hvert 5. minut
*/5 * * * * curl -s https://eksempel.dk/wp-cron.php > /dev/null

Sådan finder du flaskehalsen

Mål, før du ændrer noget. Serverens svartid på en side, der ikke er cachet, fortæller, om problemet ligger på serveren eller i browseren. Er den høj, så installér Query Monitor, helst på en kopi af siden: det viser antal og tid for databaseforespørgsler, hvilke plugins der står bag, hooks og kald til eksterne tjenester. Når det ikke er nok, viser en profiler som New Relic, Tideways eller Blackfire, hvilke PHP-funktioner tiden går i, request for request. Det var sådan, vi fandt JSON-funktionen i eksemplet ovenfor. Ret én ting ad gangen, og mål igen.

Ofte stillede spørgsmål

Gør mange plugins WordPress langsomt?

Ikke i sig selv. Et lille plugin, der kun registrerer en shortcode, koster næsten intet, mens et enkelt tungt plugin kan stå for det meste af svartiden. Mål, hvad hvert plugin gør, i stedet for at tælle dem.

Har jeg brug for Redis?

Til en blog med sidecache sjældent. Til en webshop næsten altid, fordi kurv og checkout ikke kan sidecaches, og object cachen er det, der aflaster databasen på netop de sider. Mange hostingudbydere kan slå Redis til for dig.

Skal jeg optimere eller købe bedre hosting?

Mål først. Er svartiden høj, selv på en side med få forespørgsler, eller mangler serveren OPcache og workers, er det hosting. Går tiden i databasen eller i ét plugin, hjælper en større server kun lidt, og så er det koden, der skal rettes.

Vil du have hjælp?

Vi finder, hvor tiden går, fra server og database til plugins og front end, og retter det uden at tage butikken ned. Læs mere om optimering af din WooCommerce-shop.

← Alle indlæg

Lad os høre mere om dig

Er din webshop langsom, ustabil eller bare svær at arbejde med? Skriv et par linjer, så får du en ærlig vurdering direkte fra udvikleren.