
🗑 Programmatisk opprydding av WordPress mediebibliotek: PHP-skript for å fjerne søppelfiler
WordPress mediebibliotek fungerer som et loft: du sletter et innlegg, bildene blir værende. Bytt tema, gamle bildestørrelser ligger der som dødvekt. Migrer nettstedet, halvparten av miniatyrbildene returnerer 404.
Manuell opprydding via administrasjonspanelet på et nettsted med et par tusen filer er en øvelse i meditasjon. Men det finnes en raskere måte: fem PHP-funksjoner som finner og fjerner søppel i én enkelt gjennomkjøring. Ingen utvidelser, med klar kode og kontroll over hver slettet fil.
Før du kjører: full sikkerhetskopi. Disse funksjonene sletter filer permanent: ingen papirkurv, ingen angrefunksjon. Er du i tvil, kjør på en staging-kopi først.
💡 Rask oversikt:
- Slette uforankrede vedlegg, filer som blir liggende igjen etter sletting av innlegg
- Rydde opp mediefiler fra en spesifikk egendefinert innholdstype (CPT)
- Tømme mediebiblioteket for ødelagte lenker, 404-vedlegg uten fil på serveren
- Finne og slette filer i
wp-content/uploadssom ikke er registrert som WordPress-vedlegg - Scenario for nettsteder der bilder lagres i egendefinerte felt (ACF, Meta Box), ikke som vedlegg
Hva du trenger å vite før du kjører
Koden nedenfor sletter filer fysisk, fra disk og database. Tre ting som vil redde nettstedet ditt.
For det første: bilder på tag-arkivsider eller i SEO-beskrivelser er ofte ikke knyttet til noe innlegg. De henger som «foreldreløse», men nettstedet trenger dem. Har du slike bilder, ekskluder dem fra omfanget av disse funksjonene eller juster betingelsene.
For det andre: WordPress oppretter flere størrelser av hvert bilde. Miniatyrbilder arver post_parent fra originalen, så funksjonen delete_unattached_attachments() rører dem ikke, den filtrerer strengt på post_parent = 0. Problemet oppstår bare hvis originalen selv mistet sin tilknytning til innlegget.
For det tredje: hvis en lenke til den slettede filen finnes i innholdsinnlegg, vil den bli ødelagt etter opprydding. Før du kjører, gjennomsøk nettstedet med Screaming Frog eller lignende og kartlegg lenkene.
1. Slette uforankrede vedlegg
Vanligste scenario: du slettet et innlegg, vedlegg ble værende i databasen med post_type = 'attachment' og post_parent = 0. De tar opp plass på disk og i sikkerhetskopier.
Funksjonen nedenfor finner alle slike poster og sletter dem. Plasser den i functions.php i et child theme eller via en kodesnuttutvidelse som WPCode. Den kjører ikke av seg selv, det er en definisjon som trenger et kall.
1 function delete_unattached_attachments() { 2 $attachments = get_posts( array( 3 'post_type' => 'attachment', 4 'numberposts' => -1, 5 'fields' => 'ids', 6 'post_parent' => 0, 7 ) ); 8 9 if ( $attachments ) { 10 foreach ( $attachments as $attachment_id ) { 11 $attachment_path = get_attached_file( $attachment_id ); 12 wp_delete_attachment( $attachment_id, true ); 13 unlink( $attachment_path ); 14 } 15 } 16 }
get_posts() velger alle vedlegg uten et overordnet innlegg. wp_delete_attachment() med parameteren true sletter både databaseposten og filen med miniatyrbilder. Ekstra unlink() er en forsikring: hvis filen av en eller annen grunn ble liggende igjen på disk, slettes den tvangsmessig.
Merk: utvalgte bilder har også post_parent = 0 i noen konfigurasjoner. Før produksjonskjøring, erstatt wp_delete_attachment med echo $attachment_id . '<br>', så ser du listen over ID-er som vil bli slettet. Når du har bekreftet at alt er riktig, gå tilbake til produksjonsversjonen.
Etter en enkelt kjøring, fjern funksjonen fra functions.php. Ingen grunn til å ha den liggende på hver init.
2. Slette vedlegg fra en spesifikk CPT
Tidligere WooCommerce-butikk, gammel porteføljedel, slettet egendefinert innholdstype, alle bildene deres fortsetter å ligge på serveren. Funksjonen nedenfor rydder opp vedlegg knyttet til innlegg av en spesifisert type.
1 function delete_cpt_attachments( $cpt = 'card' ) { 2 $attachments = get_posts( array( 3 'post_type' => 'attachment', 4 'numberposts' => -1, 5 ) ); 6 7 if ( $attachments ) { 8 foreach ( $attachments as $attachment ) { 9 $parent_id = $attachment->post_parent; 10 11 if ( $cpt === get_post_type( $parent_id ) ) { 12 $attachment_path = get_attached_file( $attachment->ID ); 13 wp_delete_attachment( $attachment->ID, true ); 14 unlink( $attachment_path ); 15 } 16 } 17 } 18 }
Erstatt 'card' med din CPT-slug. For WooCommerce-produkter, 'product'. Hvis CPT-en allerede er slettet, vil get_post_type() returnere false, vedlegg av den typen vil ikke bli berørt. For slettede CPT-er må logikken justeres: sjekk ikke overordnet type, men medlemskap i en taksonomi eller et metafelt.
På store databaser, vær forsiktig: 'numberposts' => -1 uten 'fields' => 'ids' laster fulle WP_Post-objekter. På 10 000+ vedlegg kan dette treffe memory_limit. For produksjonsvolumer, legg til 'fields' => 'ids' og hent bare ID-er, get_post_type() vil også fungere med overordnede ID-er.
3. Tømme mediebiblioteket for 404-vedlegg
Ødelagte miniatyrbilder i mediebiblioteket er et symptom på at filen på disk ble slettet (manuelt, ved hostingkrasj eller en buggete utvidelse), men databaseposten ble værende. WordPress viser et grått rektangel, men ved klikk, 404.
Funksjonen spør URL-en til hvert vedlegg og sletter de som returnerer 404.
1 function delete_404_attachments() { 2 $attachments = get_posts( array( 3 'post_type' => 'attachment', 4 'numberposts' => -1, 5 'fields' => 'ids', 6 ) ); 7 8 if ( $attachments ) { 9 foreach ( $attachments as $attachment_id ) { 10 $file_url = wp_get_attachment_url( $attachment_id ); 11 $file_headers = @get_headers( $file_url ); 12 13 if ( $file_headers && strpos( $file_headers[0], '404' ) !== false ) { 14 wp_delete_attachment( $attachment_id, true ); 15 } 16 } 17 } 18 }
Viktig: denne funksjonen er ressurskrevende. Hvert get_headers()-kall er en HTTP-forespørsel til din egen server. På tusen vedlegg gjør du tusen HTTP-forespørsler i én gjennomkjøring. Resultat: tregt, serverbelastning, noen hostingleverandører dreper prosessen ved tidsavbrudd.
For store mediebiblioteker, del opp i biter med 'offset' og 'numberposts' eller kjør via WP-CLI med en batch-grense. Hvis nettstedet ligger bak CDN eller proxy, erstatt sjekken med wp_remote_head() med en tidsavbrudd, get_headers() håndterer ikke alltid videresendinger korrekt og støtter ikke autentisering.
4. Omvendt sjekk: filer i uploads uten databasepost
De tre foregående funksjonene rydder databasen, sletter vedleggsposter. Men wp-content/uploads kan inneholde filer som ikke er registrert som vedlegg i det hele tatt: lastet opp via FTP, etterlatt av utvidelser, generert av cache.
Denne funksjonen går motsatt vei: ikke fra database til filer, men fra filer til database. Skanner wp-content/uploads rekursivt og sjekker for hver fil via attachment_url_to_postid() om den er et vedlegg. Hvis ikke, sletter den den.
1 function clean_uploads_from_nonattachments() { 2 $uploads_dir = wp_upload_dir(); 3 $search = $uploads_dir['basedir']; 4 $replace = $uploads_dir['baseurl']; 5 $root = $uploads_dir['basedir']; 6 7 $iter = new RecursiveIteratorIterator( 8 new RecursiveDirectoryIterator( $root, RecursiveDirectoryIterator::SKIP_DOTS ), 9 RecursiveIteratorIterator::SELF_FIRST, 10 RecursiveIteratorIterator::CATCH_GET_CHILD 11 ); 12 13 foreach ( $iter as $fileinfo ) { 14 if ( $fileinfo->isFile() ) { 15 $image_path = $fileinfo->getPathname(); 16 $image_url = str_replace( $search, $replace, $image_path ); 17 $attachment_id = attachment_url_to_postid( $image_url ); 18 19 if ( ! $attachment_id ) { 20 unlink( $image_path ); 21 } 22 } 23 } 24 }
På en testserver med 1 GB opplastinger kjørte funksjonen på 15 sekunder og frigjorde 700 MB, og etterlot 300 MB med faktisk brukte filer. For mapper større enn 5 GB, bryt skanningen etter år: erstatt $root med $uploads_dir['basedir'] . '/2025/', deretter '/2024/' og så videre.
Kjør først versjonen uten sletting, erstatt unlink( $image_path ) med echo $image_path . PHP_EOL. Du vil se hele listen over filer funksjonen anser som søppel. Sjekk visuelt, og gå deretter tilbake til unlink().
5. Scenario med egendefinerte felt: når bilder ikke er vedlegg
Det mest komplekse tilfellet: et nettsted der bilder lagres ikke som WordPress-vedlegg, men som URL-er i egendefinerte felt (ACF, Meta Box, egendefinerte temafelt). Typisk eksempel, en bokhandel: bokomslag i feltet bookcover, forfatterbilde i bookauthor_picture, listebilde i book_list_pictrue.
I denne arkitekturen vil attachment_url_to_postid() returnere 0 for alle filer i uploads. Den forrige funksjonen vil slette alt, inkludert faktisk brukte bilder. En annen tilnærming er nødvendig.
5.1. Bygge en hviteliste
Samle først URL-er for alle bilder fra alle nødvendige egendefinerte felt. I eksemplet nedenfor, tre CPT-er og tre felt:
1 $all_good_pictures = array(); 2 3 // Book covers (CPT 'post', field 'bookcover') 4 $posts = get_posts( array( 5 'post_type' => 'post', 6 'posts_per_page' => -1, 7 'post_status' => 'any', 8 'fields' => 'ids', 9 ) ); 10 foreach ( $posts as $post_id ) { 11 $cover = get_field( 'bookcover', $post_id ); 12 if ( $cover ) { 13 $all_good_pictures[] = $cover; 14 } 15 } 16 17 // List images (CPT 'book_list', field 'book_list_pictrue') 18 $lists = get_posts( array( 19 'post_type' => 'book_list', 20 'posts_per_page' => -1, 21 'post_status' => 'any', 22 'fields' => 'ids', 23 ) ); 24 foreach ( $lists as $list_id ) { 25 $pic = get_field( 'book_list_pictrue', $list_id ); 26 if ( $pic ) { 27 $all_good_pictures[] = $pic; 28 } 29 } 30 31 // Author photos (CPT 'bookauthor', field 'bookauthor_picture') 32 $authors = get_posts( array( 33 'post_type' => 'bookauthor', 34 'posts_per_page' => -1, 35 'post_status' => 'any', 36 'fields' => 'ids', 37 ) ); 38 foreach ( $authors as $author_id ) { 39 $pic = get_field( 'bookauthor_picture', $author_id ); 40 if ( $pic ) { 41 $all_good_pictures[] = $pic; 42 } 43 } 44 45 $all_good_pictures = array_filter( $all_good_pictures );
På et reelt prosjekt, en bokhandel, gjorde denne tilnærmingen det mulig å beregne de fleste søppelfiler og frigjøre en betydelig del av diskplassen.
5.2. Slett alt som ikke er i hvitelisten
Gå nå gjennom wp-content/uploads og slett hver fil som ikke er i $all_good_pictures:
1 $uploads_dir = wp_upload_dir(); 2 $search = $uploads_dir['basedir']; 3 $replace = $uploads_dir['baseurl']; 4 $root = $uploads_dir['basedir']; 5 6 $iter = new RecursiveIteratorIterator( 7 new RecursiveDirectoryIterator( $root, RecursiveDirectoryIterator::SKIP_DOTS ), 8 RecursiveIteratorIterator::SELF_FIRST, 9 RecursiveIteratorIterator::CATCH_GET_CHILD 10 ); 11 12 foreach ( $iter as $fileinfo ) { 13 if ( $fileinfo->isFile() ) { 14 $image_path = $fileinfo->getPathname(); 15 $image_url = str_replace( $search, $replace, $image_path ); 16 17 if ( ! in_array( $image_url, $all_good_pictures, true ) ) { 18 unlink( $image_path ); 19 } 20 } 21 }
in_array()-metoden med streng sammenligning på en matrise med 10 000+ elementer er ikke den raskeste. For produksjonsvolumer, erstatt den vanlige matrisen med en assosiativ en: $all_good_pictures = array_fill_keys( $all_good_pictures, true ) og sjekk via isset(). Forskjellen på 40 000 elementer, fra titalls sekunder til brøkdeler av et sekund.
Hvordan kjøre disse funksjonene
Alle kodesnuttene over er funksjonsdefinisjoner. De gjør ingenting før du kaller dem. Tre trygge måter å kjøre på:
Metode | Når du skal bruke | Tilbakerulling |
|---|---|---|
WP-CLI | Engangsopprydding, konsolltilgang | Nei, kun sikkerhetskopi |
Hook | Ingen konsoll, trenger å kjøre fra admin | Nei, kun sikkerhetskopi |
Kodesnuttutvidelse (WPCode) | Praktisk lagring og aktiver/deaktiver | Deaktiver kodesnutt, funksjon inaktiv |
Eksempel på engangskjøring via admin:
1 add_action( 'admin_init', 'run_cleanup_once' ); 2 function run_cleanup_once() { 3 if ( isset( $_GET['cleanup'] ) && 'confirmed' === $_GET['cleanup'] ) { 4 delete_unattached_attachments(); 5 } 6 }
Besøk https://yoursite.com/wp-admin/?cleanup=confirmed, funksjonen kjører én gang. Etter utførelse, fjern kodesnutten.
For WP-CLI, den anbefalte metoden for produksjon, lagre funksjonskoden til en midlertidig fil og kjør:
1 wp eval-file cleanup.php
Før opprydding er det nyttig å se prosessen visuelt. I videoen nedenfor, en trinnvis gjennomgang av opprydding av WordPress-mediebiblioteket med manuelle og automatiske metoder.
⁉️🤔 Ofte stilte spørsmål
Kan filer gjenopprettes etter sletting?
Nei. Funksjoner bruker
wp_delete_attachment()medtrueogunlink(), filer slettes fysisk, utenom papirkurven. Eneste forsikring: full sikkerhetskopi av filer og database før kjøring. Sjekk om hostingleverandøren din har automatiske daglige sikkerhetskopier, hos Kinsta, WP Engine og SiteGround er de aktivert som standard. Dette gir et ekstra gjenopprettingspunkt i tillegg til din manuelle sikkerhetskopi.
Hvorfor virket ikke funksjonen, filene ble liggende?
Vanligste årsak: du la til funksjonsdefinisjonen i
functions.php, men kalte den ikke. Enfunction ... { }-blokk er bare en instruksjon. For at koden skal utføres, må funksjonen kobles til en hook viaadd_action()eller kjøres manuelt via WP-CLI. I «Hvordan kjøre»-delen, tre metoder, velg basert på ditt servertilgangsnivå.
Vil funksjonen slette miniatyrbilder av brukte bilder hvis de er uforankrede vedlegg?
Nei. Miniatyrbilder (thumbnail, medium, large) har samme
post_parentsom det opprinnelige vedlegget. Funksjonen filtrerer strengt påpost_parent = 0, bare poster uten noe overordnet innlegg i det hele tatt. Størrelser av originaler arverpost_parentog havner ikke i utvalget. Problemet oppstår bare hvis originalen selv mistet sitt vedlegg, da vil funksjonen slette den sammen med alle størrelser.
Hva om noen bilder er i egendefinerte felt og noen er vanlige vedlegg?
Kombiner tilnærminger fra seksjon 4 og 5. Samle først en hviteliste fra egendefinerte felt (seksjon 5.1). Så når du skanner uploads (seksjon 4) for hver fil, sjekk begge betingelsene: er filen et WordPress-vedlegg via
attachment_url_to_postid()OG er den til stede i hvitelisten. En fil slettes bare hvis ingen av betingelsene er oppfylt:if ( ! $attachment_id && ! isset( $good_pictures[ $image_url ] ) ) { unlink( $image_path ); }.
Hvor trygt er dette for et WooCommerce-nettsted?
WooCommerce lagrer produktbilder som standard WordPress-vedlegg, de er knyttet til innholdstypen
product. Slettefunksjonen for uforankrede vedlegg (seksjon 1) vil ikke røre dem. Men funksjonen for en spesifikk CPT (seksjon 2), jo, hvis du sender inn'product'. For WooCommerce er det tryggeste metoden fra seksjon 5 (hviteliste): den opererer på hva som faktisk er i bruk, ikke hva som er knyttet. Før kjøring, eksporter ID-er for alle produktvedlegg for kryssjekking.
Hva du skal bruke på ditt prosjekt: endelig oversikt
Metodevalg avhenger av nettstedets arkitektur:
- Standard blogg eller nyhetsnettsted, funksjoner fra seksjon 1 (uforankrede vedlegg) og seksjon 3 (404-vedlegg) er nok. Kjør en gang hver sjette måned, mediebiblioteket vil være i orden.
- Nettsted med gamle CPT-er (portefølje, katalog, rubrikkannonser), legg til seksjon 2. Rydd presist opp søppel fra slettede eller forlatte innholdstyper.
- Prosjekt på ACF/Meta Box med egendefinerte felt for bilder, ditt valg: seksjon 5. Samle en hviteliste, slett resten. Sett opp én gang, og gjenta deretter etter behov.
- Alt sammen og uklart, start med rekursiv skanning av uploads (seksjon 4). Se hvor mye søppel som ligger på disk. Bruk deretter seksjon 1-3 og 5 selektivt etter situasjon.
Ingen av disse skriptene erstatter regelmessig netthygiene. Men når du først har skrevet den nødvendige funksjonen og lagret den i prosjektdokumentasjonen, vil du spare timer med manuelt arbeid ved neste revisjon.
Og ja, du har allerede tatt en sikkerhetskopi.




