Je sleept een SVG-icoon, een WebP-foto of een HEIC vanaf je iPhone naar de mediabibliotheek en WordPress weigert het bestand met Sorry, dit bestandstype is niet toegestaan om veiligheidsredenen. (of, op oudere sites, de variant Sorry, je hebt geen toestemming om dit bestandstype te uploaden.). Het bestand mankeert niets. Het account heeft volledige editor- of beheerrechten. De fout gaat niet over de inhoud van het bestand of over jouw rechten in WordPress. Hij gaat erover of het MIME-type van het bestand voorkomt op de whitelist die WordPress raadpleegt bij elke upload, en die lijst is bewust conservatief.
Wat het verwarrend maakt: de juiste fix is voor bijna ieder bestandstype anders. SVG is met opzet geblokkeerd en heeft sanitisatie nodig, niet alleen een whitelist-vermelding. WebP werkt sinds WordPress 5.8 standaard, maar alleen als de image library op de server het kan. HEIC werkt sinds WordPress 6.7, maar alleen als Imagick met HEIC is gecompileerd. De ALLOW_UNFILTERED_UPLOADS-constante lijkt een fix in één regel en dat is hij ook, alleen is de prijs dat een gehackt admin-account direct een uitvoerbare PHP-file via de media-uploader kan dumpen. Dit artikel legt uit wat de melding eigenlijk betekent, geeft de canonieke fix met upload_mimes voor legitieme custom-formaten, en loopt vervolgens per formaat de juiste aanpak door voor de drie types waar mensen het vaakst tegenaan lopen.
Waarom WordPress bestandstypes überhaupt blokkeert
De upload-controle bestaat omdat de mediabibliotheek vanuit het oogpunt van de server een ongeauthenticeerde upload-directory is. Alles wat daar terechtkomt krijgt een stabiele URL die iedereen op het internet kan opvragen. Willekeurige bestandstypes toestaan verandert die map in een plek waar een admin (of een aanvaller die een admin heeft gehackt) een .php shell, een HTML-file met JavaScript erin of een SVG met een <script>-blok kan plaatsen, en het ding daarna met één GET-request kan triggeren. De whitelist is de scheidslijn tussen "gebruiker uploadt foto's" en "gebruiker uploadt een backdoor".
Onder de motorkap loopt elke upload in WordPress door twee opeenvolgende controles. Eerst geeft het upload_mimes-filter (dat bestaat sinds WordPress 2.0) een lijst terug met toegestane extensies en hun MIME-types. Die lijst komt uit wp_get_mime_types() en wordt vervolgens nog ingekort op basis van capabilities: gebruikers zonder unfiltered_html raken htm|html en js kwijt, en swf en exe worden voor iedereen verwijderd, ongeacht rol. Daarna leest wp_check_filetype_and_ext() (sinds WordPress 3.0) de werkelijke inhoud van het bestand met PHP finfo en getimagesize, en checkt of de bytes overeenkomen met de geclaimde extensie. Een bestand moet door beide controles heen om geaccepteerd te worden. Die tweede controle is precies de reden dat alleen SVG aan de whitelist toevoegen op veel hosts niet werkt, zoals verderop in dit artikel.
De canonieke fix: een MIME-type toevoegen via upload_mimes
Voor de meeste legitieme custom-formaten (een vendor-formaat dat je bedrijf gebruikt, een eigen webfont, een .zip die je telkens geweigerd ziet) is de juiste fix een klein upload_mimes-filter in een must-use plugin of in functions.php. Het patroon staat in de reference voor de upload_mimes-hook en ziet er zo uit:
// wp-content/mu-plugins/allow-extra-mimes.php
// WordPress 6.5+, PHP 8.1+
add_filter( 'upload_mimes', function ( $mimes ) {
$mimes['woff'] = 'font/woff';
$mimes['woff2'] = 'font/woff2';
return $mimes;
} );
Twee dingen waar mensen op stuk lopen. De array-key is de bestandsextensie (of een regex van extensies, gescheiden met |, zoals WordPress core dingen registreert als 'jpg|jpeg|jpe' => 'image/jpeg'). De waarde is het canonieke MIME-type. Dat je het bestand in mu-plugins zet en niet in functions.php betekent dat een themewissel of een theme-update het filter niet kan weghalen en de uploads opnieuw kan breken.
Je weet dat de fix gewerkt heeft als je een .woff2-bestand kunt uploaden vanuit de mediabibliotheek en het bestand zonder fout in het raster verschijnt. Lukt het nog steeds niet, dan gooit de tweede controle (wp_check_filetype_and_ext) het er alsnog uit omdat PHP's finfo een MIME terugkreeg die niet matcht met de extensie. Dat is zeldzaam voor fonts, maar wel vaak het verhaal bij SVG.
Waarom upload_mimes alleen niet genoeg is voor SVG
SVG is het bestandstype waar naïeve fixes breken en zelfs zorgvuldige fixes onveilig zijn. Het verdient een eigen sectie. De technische reden dat een upload_mimes-filter alleen niet werkt voor SVG, is dat SVG XML is en geen binair beeldformaat. PHP's exif_imagetype() ziet het niet. PHP's finfo geeft alleen image/svg+xml terug als de magic-database van het systeem een SVG-entry heeft, en dat verschilt per host. Geeft finfo niets bruikbaars terug, dan besluit wp_check_filetype_and_ext() dat de inhoud niet matcht met de extensie en wordt het bestand alsnog geweigerd.
Deze regressie heeft een eigen historie. WordPress 4.7.1, uitgebracht in januari 2017 als security release, schroefde de content-versus-extensie-validatie in wp_check_filetype_and_ext() aan. De wijziging brak SVG, DOCX, PPT, XLSM, AI en nog wat andere plugin-ondersteunde formaten waarvan de binaire of XML-payload niet meer door de nieuwe image-MIME-check kwam. Trac-ticket #39552 hield de SVG-kant bij en #39550 de bredere non-image regressie. De aanpassingen erop landden in 4.7.3. Niets daarvan betekent dat SVG aan de standaardlijst werd toegevoegd of eraf werd gehaald. SVG zat nooit in de defaults. Wat veranderde, is dat plugin-only workarounds die alleen upload_mimes haakten zonder ook wp_check_filetype_and_ext te haken, niet meer werkten.
De minimale code die door beide controles heen komt, ziet er zo uit:
// DIT IS ONVEILIG ZONDER SANITISATIE. BLIJF LEZEN.
// WordPress 6.5+, PHP 8.1+
add_filter( 'upload_mimes', function ( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
$mimes['svgz'] = 'image/svg+xml';
return $mimes;
} );
add_filter( 'wp_check_filetype_and_ext', function ( $data, $file, $filename, $mimes ) {
if ( ! $data['type'] ) {
$filetype = wp_check_filetype( $filename, $mimes );
if ( 'svg' === $filetype['ext'] ) {
$data['ext'] = 'svg';
$data['type'] = 'image/svg+xml';
}
}
return $data;
}, 10, 4 );
Dat snippet is wat de meeste "SVG inschakelen in WordPress"-tutorials je geven en waarna ze stoppen. Het is genoeg om uploads werkend te krijgen. Het is niet genoeg om ze veilig te krijgen. Het bestand dat in wp-content/uploads/ belandt, is precies wat de gebruiker indiende, inclusief <script>-blokken, onload-attributen en externe entity-referenties. Als een bezoeker die SVG in de browser opvraagt, wordt die JavaScript uitgevoerd in de context van jouw domein. Dit is precies de privilege escalation waarvoor de whitelist gebouwd is.
SVG, dan wel goed: de Safe SVG-plugin
De juiste manier om SVG-uploads in WordPress aan te zetten, is Safe SVG van 10up installeren. De plugin heeft meer dan een miljoen actieve installaties en wordt onderhouden door hetzelfde bureau achter een aantal andere veelgebruikte WordPress-libraries. Safe SVG haakt zowel upload_mimes als wp_check_filetype_and_ext correct, en (dit is het stuk dat ertoe doet) elke geüploade SVG wordt door de enshrined/svg-sanitizer PHP-library gehaald voordat het bestand naar schijf wordt geschreven. De sanitizer strip <script>-elementen, event handlers (onload, onclick, enzovoort), JavaScript-URL's in href-attributen, en externe entity-referenties die voor XXE gebruikt kunnen worden. De plugin geeft daarnaast thumbnail-previews in de mediabibliotheek en laat je uploads beperken tot specifieke rollen.
Dat de plugin het serieus neemt zie je terug in de update-historie: een patch in augustus 2025 heeft svg-sanitizer naar 0.22.0 gebumpt om een case-insensitive attribuut-bypass te dichten, precies het soort edge case waarom je een onderhouden sanitizer wilt en niet zelf gaat schrijven.
Installeren: ga in wp-admin naar Plugins: Nieuwe plugin, zoek op "Safe SVG", installeer en activeer. Configuratie is niet nodig om sanitisatie in werking te laten treden. Je weet dat het werkt als je een SVG kunt uploaden, een thumbnail-preview ziet in de mediabibliotheek, en (dit is de echte security-check) als je het bestand direct opvraagt en bevestigt dat eventuele <script>-blokken uit de source zijn gehaald. Test toch even met een SVG die expliciet vijandig is, niet met een schone van een designer.
Lukt de plugin niet (een strikte regel tegen third-party plugins, een multisite met afgeschermde plugin-lijsten), dan is de fallback om svg-sanitizer handmatig te integreren in een must-use plugin en aan te roepen vanuit de wp_handle_upload_prefilter-hook. Dat is meer werk en levert hetzelfde resultaat. Het is zelden de moeite waard.
WebP: standaard sinds WordPress 5.8, maar de server moet meewerken
WebP werd toegevoegd aan de standaard toegestane MIME-types in WordPress 5.8, uitgebracht in juli 2021. Op een actuele WordPress-installatie heb je geen upload_mimes-filter voor WebP nodig. Als de upload alsnog faalt met de niet-toegestaan-melding, is een van drie dingen aan de hand.
Het eerste: je zit nog op een release van vóór 5.8. Check Dashboard: Updates en draai eventuele wachtende core-updates. De default-MIME-wijziging zit in 5.8 en wordt niet teruggebackport.
Het tweede: je zit op een multisite die vóór 5.8 is aangemaakt en die op netwerkniveau MIME-instellingen heeft die nog op de oude defaults staan. Multisite slaat zijn toegestane MIME-lijst op netwerkniveau op onder Netwerkbeheer: Instellingen: Netwerkinstellingen: Upload-instellingen. De standaardlijst daar updatet niet automatisch mee als een nieuwe core-release een formaat toevoegt. Voeg webp toe aan het veld Geüploade bestandstypes en sla op.
Het derde (en meest voorkomende): de upload zelf slaagt, maar WordPress kan het bestand niet verwerken omdat de image library van de server geen WebP kan encoden. In dat geval is het symptoom een andere foutmelding of een kapotte thumbnail, niet de niet-toegestaan-tekst. WebP-ondersteuning vereist Imagick of GD gecompileerd met WebP. Check Gereedschap: Sitediagnose: Info: Mediaverwerking om te zien of WebP onder de actieve image editor wordt vermeld als ondersteund formaat. Staat het er niet bij, dan zit de fix server-side: vraag je host om WebP in te schakelen, of installeer op een eigen VPS de juiste system library en herbouw de PHP-extensie. Het bredere probleem van kapotte thumbnails na een geslaagde upload behandel ik in mijn artikel over de WordPress-mediabibliotheek die geen afbeeldingen toont.
HEIC: standaard sinds WordPress 6.7, met verplichte JPEG-conversie
HEIC en HEIF (het iPhone-fotoformaat en zijn container) werden toegevoegd aan de standaard MIME-types in WordPress 6.7, uitgebracht in november 2024. De release voegde alle vier de gerelateerde MIME-types toe: image/heic, image/heif, image/heic-sequence en image/heif-sequence. Daarnaast is er een helper, wp_is_heic_image_mime_type(), en een nieuwe conversie-pipeline die server-side draait op het moment van uploaden.
Het mechanisme dat ertoe doet. WordPress 6.7 slaat HEIC-bestanden niet op als HEIC. Bij het uploaden zet WordPress het bestand om naar JPEG met Imagick (mits Imagick met HEIC-ondersteuning is gecompileerd, wat meestal ImageMagick 7.0.8-26 of nieuwer betekent). Het originele HEIC wordt na conversie verwijderd en alleen de JPEG blijft staan, met een download-link naar het origineel op de attachment-pagina. Dat is met opzet zo. Browsers renderen HEIC niet native, dus een HEIC opslaan en direct serveren zou voor de meeste bezoekers een kapotte <img>-tag opleveren.
Drie dingen om te weten.
Ten eerste: het conversie-doel is altijd JPEG. Niet WebP of AVIF. JPEG is gekozen als universeel ondersteunde minste gemene deler. Je kunt het overrulen met het image_editor_output_format-filter als je een sterke voorkeur hebt, maar de meeste sites zijn beter af door het met rust te laten.
Ten tweede: ondersteunt Imagick op je server geen HEIC, dan accepteert WordPress 6.7 de upload niet stilletjes. Het toont een waarschuwing die aanraadt om handmatig te converteren. De niet-toegestaan-melding op een 6.7-site komt dan ook bijna altijd doordat je nog op een Imagick-build zit van vóór HEIC. Gereedschap: Sitediagnose: Info: Mediaverwerking vertelt je of HEIC-conversie op de huidige server beschikbaar is.
Ten derde: op WordPress vóór 6.7 zat HEIC nooit in de defaults en is er geen schone fix. Je kunt image/heic aan upload_mimes toevoegen en het bestand accepteren, maar WordPress converteert het niet, browsers kunnen het niet renderen, en het resultaat is een kapotte thumbnail in de bibliotheek. De realistische opties zijn upgraden naar WordPress 6.7 of nieuwer, of HEIC's op je laptop converteren naar JPEG voordat je ze upload. Een third-party "HEIC Support"-plugin kan dit op oudere WordPress-versies ook nog wel afdekken.
Je weet dat het werkt als je op WordPress 6.7+ een HEIC upload, het bestand als JPEG met normale thumbnails in de bibliotheek verschijnt, en de attachment-pagina een download-link naar het originele HEIC laat zien.
ALLOW_UNFILTERED_UPLOADS: de noodgreep en waarom hij niet permanent moet zijn
Er is één constante die de hele MIME-whitelist passeert. Deze regel toevoegen aan wp-config.php geeft de huidige gebruiker de unfiltered_upload-capability en laat de niet-toegestaan-melding voor alles verdwijnen:
// wp-config.php
// Tijdelijk. Verwijder dit nadat de migratie klaar is.
define( 'ALLOW_UNFILTERED_UPLOADS', true );
De capability-check is geïmplementeerd in wp-includes/capabilities.php en treedt alleen in werking als de constante is gedefinieerd, en (op multisite) alleen voor Super Admins, ongeacht rol. De multisite-specifiek behandel ik hieronder.
Als tijdelijke tool is dit echt bruikbaar. Migreer je een site en moet je een map met gemengde bestandstypes bulk-importeren waarvoor het geen kant-en-klaar upload_mimes-filter verdient, definieer dan de constante, doe de import, en haal de regel daarna weer weg. De reden dat het een slechte permanente fix is, is recht voor zijn raap: het staat álle bestandstypes toe, inclusief PHP-scripts. Compromitteert een aanvaller een admin-account op een site waar deze constante aan is blijven staan, dan is een plugin uploaden of een thema bewerken niet meer nodig om code-execution te krijgen. Ze openen de media-uploader, slepen er een shell.php in, en vragen die direct op onder /wp-content/uploads/2026/04/shell.php. Geen exploit chain nodig.
Het patroon dat ik zelf gebruik, en dat ik aanraad: definieer 'm, gebruik 'm, haal 'm hetzelfde uur weg. Behandel het zoals je root-SSH op een server tijdelijk inschakelen behandelt. Niet "voor altijd uit, nooit gebruiken", wel "standaard uit, kort aan als er een goede reden is, weer uit als de klus klaar is". Verschillende hosting- en security-writeups (waaronder PreventDirectAccess en de KB van Kinsta) maken hetzelfde punt.
Op de lange termijn is de juiste fix altijd de gerichte: een per-formaat upload_mimes-entry voor legitieme vendor-formaten, Safe SVG voor SVG, een actuele WordPress-versie voor WebP en HEIC. Die wijzigingen accepteren precies de bestanden die je nodig hebt, zonder de deur voor de rest open te zetten.
Hoe ALLOW_UNFILTERED_UPLOADS zich gedraagt op Multisite
Op WordPress Multisite heeft de constante een eigenaardigheid die je moet kennen. Zelfs als hij is gedefinieerd, geeft ALLOW_UNFILTERED_UPLOADS de unfiltered_upload-capability uitsluitend aan Super Admins. Network Admins, site-beheerders, editors en authors krijgen 'm niet, hoe hard je de constante ook zet. De relevante capability-check ziet er in core ongeveer zo uit:
if ( defined( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS
&& ( ! is_multisite() || is_super_admin( $user_id ) ) ) {
// grant unfiltered_upload
}
Dat gedrag is bewust, ook al heeft het in trac-ticket #45818 door de jaren genoeg mensen op het verkeerde been gezet om er een issue voor te openen. Op multisite is de aanname dat de netwerk-eigenaar de enige is die de upload-gate mag passeren; site-admins niet. Ben je site-admin op een multisite die met ALLOW_UNFILTERED_UPLOADS een eigen formaat probeert te uploaden en werkt het niet, dan is dit waarom. De juiste fix op multisite is dezelfde als op single-site: voeg het formaat toe via upload_mimes (netwerk-breed als dat kan, of als mu-plugin), en gebruik Safe SVG voor SVG.
Veelvoorkomende bestandstypes en hun MIME-strings
Schrijf je een eigen upload_mimes-filter, dan zijn dit de canonieke MIME-types voor de formaten die het vaakst langskomen. WordPress accepteert de extensie als array-key (met | om equivalenten te scheiden) en het MIME-type als waarde. De volledige standaardlijst staat in wp_get_mime_types() in core.
| Extensie | MIME-type | Notitie |
|---|---|---|
svg, svgz |
image/svg+xml |
Gebruik Safe SVG, schakel niet rauw in |
webp |
image/webp |
Standaard sinds WP 5.8, server-image-library moet het ondersteunen |
avif |
image/avif |
Standaard sinds WP 6.5, vereist AVIF in Imagick of PHP 8.1+ GD |
heic, heif |
image/heic, image/heif |
Standaard sinds WP 6.7, vereist Imagick met HEIC |
woff, woff2 |
font/woff, font/woff2 |
Custom webfonts |
ttf, otf |
font/ttf, font/otf |
Desktop-fontformaten |
eot |
application/vnd.ms-fontobject |
Legacy IE-fontformaat, tegenwoordig zelden nodig |
mp4, m4v |
video/mp4 |
Standaard toegestaan |
mov |
video/quicktime |
Standaard toegestaan |
webm |
video/webm |
Standaard toegestaan |
psd |
image/vnd.adobe.photoshop |
Designer-asset, niet standaard toegestaan |
ai |
application/postscript |
Adobe Illustrator, niet standaard toegestaan |
zip |
application/zip |
Standaard toegestaan, soms door host-security alsnog geblokkeerd |
xml |
application/xml of text/xml |
Standaard toegestaan |
Twee praktische opmerkingen bij het uitbreiden van de lijst. De MIME-types moeten matchen met wat PHP's finfo voor de daadwerkelijke bestandsinhoud teruggeeft, anders sneuvelt de upload alsnog op de tweede controle, ook na het toevoegen van de entry. Faalt de upload na een schoon filter nog steeds, dan is de oorzaak vrijwel altijd een finfo-mismatch en is de fix hetzelfde wp_check_filetype_and_ext-patroon dat ik bij SVG liet zien, aangepast op jouw formaat.
Wanneer hulp inschakelen
Heb je de juiste fix voor jouw bestandstype doorgelopen en wordt de upload nog steeds geweigerd, verzamel dan onderstaande informatie voordat je een supportticket opent. Het eerste wat een ervaren engineer vraagt, is precies dit lijstje:
- Je WordPress-versie, PHP-versie en de actieve image editor met ondersteunde formaten uit Gereedschap: Sitediagnose: Info: Mediaverwerking.
- Het exacte bestand dat je probeerde te uploaden, inclusief de werkelijke extensie en het MIME-type zoals
file --mime-type yourfile.svg(of een equivalent) het rapporteert. - De exacte tekst van de foutmelding (de twee varianten zijn
Sorry, dit bestandstype is niet toegestaan om veiligheidsredenen.enSorry, je hebt geen toestemming om dit bestandstype te uploaden.). - Of de gebruiker die probeert te uploaden Beheerder, Super Admin (multisite), Editor of een andere rol heeft.
- Een kopie van het eventuele eigen
upload_mimes-filter dat je al hebt toegevoegd, plus in welk bestand het staat. - Of de site single-site of multisite is, en bij multisite wat er nu in Geüploade bestandstypes onder Netwerkinstellingen staat.
- Of
ALLOW_UNFILTERED_UPLOADSop dit moment inwp-config.phpis gedefinieerd.
Stuur dat allemaal in je eerste bericht, en het ticket landt zonder rondje terug bij de juiste engineer. Zie je dat dezelfde root cause meer media-problemen veroorzaakt, dan dekken twee aangrenzende artikelen specifieke kanten van dezelfde probleemruimte: het artikel over HTTP error tijdens media upload gaat over uploads die met een generieke HTTP-fout sneuvelen na de MIME-check, en het artikel over maximale uploadgrootte verhogen gaat over de aparte upload_max_filesize-limiet die een andere foutmelding oplevert maar uiteindelijk hetzelfde resultaat.
Voor de bredere context, dat de upload-whitelist één laag is in een verdedigd geheel en niet de enige laag, zie mijn artikel over WordPress beveiliging verharden.
Hoe je voorkomt dat het terugkomt
De meeste vermijdbare gevallen delen één patroon: een eenmalige fix die de directe melding wegpoetst maar de site minder veilig achterlaat dan hij was. Drie gewoontes voorkomen het grootste deel ervan.
- Voeg MIME-types één voor één toe, in
mu-plugins, met een commentaarregel waarom. Eénadd_filter( 'upload_mimes', ... )-aanroep inwp-content/mu-plugins/allow-extra-mimes.phpis makkelijker te auditen dan een constante inwp-config.phpen overleeft themewissels. - Schakel rauwe SVG-uploads nooit in, ook niet tijdelijk. Installeer Safe SVG. De kosten van een plugin installeren zijn lager dan de kosten van één stored XSS, en "ik sanitiseer het later wel handmatig" is het soort plan dat een deadline niet overleeft.
- Behandel
ALLOW_UNFILTERED_UPLOADSals een tijdelijk wachtwoord. Definieer 'm, gebruik 'm, haal 'm in dezelfde sessie weg. Vind je 'm in eenwp-config.phpdie je geërfd hebt en kan niemand uitleggen waarom hij er staat, dan hoort hij er niet te staan.