# Så hanterar PHP filuppladdningar

> Så hanterar PHP filuppladdningar internt: multipart/form-data, temporära filer i upload_tmp_dir, arrayen $_FILES, gränsvärden och felkoder förklarade.

Källa: https://phpsos.de/sv/php/datei-uploads-intern/  
Aktualitet: 2026-10-25  
Leverantör: PHP SOS, PHP-nödhjälp och serversäkerhet. Omedelbar hjälp via WhatsApp: https://wa.me/491736494914?text=Hej%20PHP%20SOS%2C%20vi%20beh%C3%B6ver%20hj%C3%A4lp%20med%20v%C3%A5r%20PHP-server.%20F%C3%B6retag%3A%20%E2%80%A6%20Webbplats%3A%20%E2%80%A6

**Kort sagt:** När en webbläsare laddar upp en fil skickar den en POST-förfrågan i formatet `multipart/form-data`, där formulärfält och filinnehåll står som enskilda delar efter varandra. PHP delar själv upp förfrågan, skriver varje fildel till en temporär fil i `upload_tmp_dir`, för in uppgifterna i `$_FILES` och startar först därefter ert skript. Skriptet flyttar filen med `move_uploaded_file()`; allt som inte flyttas raderar PHP i slutet av förfrågan.

## En fils väg från webbläsaren till skriptet

För beslutsfattare är en sak viktigast: En uppladdning är inte något som bara applikationen sköter. En stor del av arbetet sker i PHP-tolken innan koden i CMS eller butik alls är inblandad. Därför påverkar fel i den här delen av PHP varje webbplats, oavsett om den har ett uppladdningsformulär eller inte.

Förloppet i korthet:

1. Formuläret med `method="POST"` och `enctype="multipart/form-data"` skickas.
2. Webbservern tar emot förfrågan och för den vidare till PHP, beroende på driftläge via PHP-FPM, mod_php eller ett annat gränssnitt.
3. PHP kontrollerar gränsvärden som `post_max_size` och delar upp innehållet med hjälp av en avgränsande teckenföljd, den så kallade boundary.
4. Vanliga fält hamnar i `$_POST`, fildelar skrivs till temporära filer och beskrivs i `$_FILES`.
5. Först nu startar skriptet, kontrollerar filen och flyttar den till målplatsen.
6. I slutet av förfrågan raderar PHP alla temporära filer som inte har flyttats.

## multipart/form-data: uppbyggnaden av en uppladdningsförfrågan

En uppladdningsförfrågan är ren text med inbäddade fildata. Headern `Content-Type` anger boundary, och i innehållet skiljer den teckenföljden de enskilda delarna åt. Varje del har egna rubrikrader med fältnamn och eventuellt filnamn. Ett kraftigt förkortat, ofarligt exempel:

```http
POST /bewerbung HTTP/1.1
Content-Type: multipart/form-data; boundary=----trenner42

------trenner42
Content-Disposition: form-data; name="vorname"

Erika
------trenner42
Content-Disposition: form-data; name="lebenslauf"; filename="lebenslauf.pdf"
Content-Type: application/pdf

%PDF-1.7 ... Dateiinhalt ...
------trenner42--
```

Alla uppgifter i den här texten kommer från avsändaren, även filnamnet och delens `Content-Type`. PHP för över dem till `$_FILES` men kontrollerar inte om de är rimliga.

## Arrayen $_FILES

För ett fält med namnet `lebenslauf` innehåller `$_FILES['lebenslauf']` enligt PHP-manualen dessa nycklar:

| Nyckel | Innehåll | Pålitlig? |
|---|---|---|
| `name` | ursprungligt filnamn hos avsändaren | nej |
| `full_path` | sökväg som webbläsaren rapporterat, sedan PHP 8.1.0 | nej |
| `type` | MIME-typ enligt webbläsaren, kontrolleras inte av PHP | nej |
| `size` | storlek i byte | fastställs av PHP |
| `tmp_name` | sökväg till den temporära filen på servern | tilldelas av PHP |
| `error` | felkod från uppladdningsbearbetningen | sätts av PHP |

För fält med hakparenteser i namnet, till exempel `bilder[]`, innehåller varje nyckel en array med en post per fil. Applikationen går då till exempel igenom `$_FILES['bilder']['error']` och når `tmp_name` och `name` via samma index.

Ett formulär kan dessutom skicka ett dolt fält `MAX_FILE_SIZE` före filfältet. Enligt manualen är det bara ett tips till webbläsaren som lätt kringgås. Ni kan uteslutande förlita er på de gränser som servern genomdriver: i PHP, i webbservern och i själva applikationen.

## Gränsvärden: php.ini-direktiven för uppladdningar

De här direktiven styr bearbetningen av uppladdningar. Standardvärdena kommer från PHP-manualen och gäller när ingen php.ini åsidosätter dem.

| Direktiv | Standard | Ändringsbar i |
|---|---|---|
| `file_uploads` | `1` | bara systemkonfigurationen |
| `upload_tmp_dir` | tom (system-temp) | bara systemkonfigurationen |
| `upload_max_filesize` | `2M` | php.ini, pool, katalogkonfiguration |
| `post_max_size` | `8M` | php.ini, pool, katalogkonfiguration |
| `max_file_uploads` | `20` | php.ini, pool, katalogkonfiguration |
| `enable_post_data_reading` | `1` | php.ini, pool, katalogkonfiguration |

`post_max_size` måste vara större än `upload_max_filesize`, och `memory_limit` bör enligt manualen i regel vara större än `post_max_size`. Går det inte att skriva till `upload_tmp_dir` väljer PHP i stället systemets temp-katalog. Med `open_basedir` måste temp-katalogen vara tillåten, annars misslyckas uppladdningarna. Praktiska värden och deras följder beskrivs i inlägget [Begränsa post_max_size och upload_max_filesize rimligt](https://phpsos.de/sv/blog/post-max-size-upload-limits/).

## Tolka felkoder rätt

Fältet `error` säger vad som hände under bearbetningen. Konstanterna går från `UPLOAD_ERR_OK` (0) via `UPLOAD_ERR_INI_SIZE` (1), `UPLOAD_ERR_FORM_SIZE` (2), `UPLOAD_ERR_PARTIAL` (3) och `UPLOAD_ERR_NO_FILE` (4) till `UPLOAD_ERR_NO_TMP_DIR` (6), `UPLOAD_ERR_CANT_WRITE` (7) och `UPLOAD_ERR_EXTENSION` (8).

```php
<?php
declare(strict_types=1);

$datei = $_FILES['lebenslauf'] ?? null;
if ($datei === null) {
    exit('Keine Upload-Daten, eventuell wurde post_max_size überschritten.');
}

$meldung = match ($datei['error']) {
    UPLOAD_ERR_OK => null,
    UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'Datei zu groß.',
    UPLOAD_ERR_NO_FILE => 'Keine Datei gewählt.',
    default => 'Upload fehlgeschlagen.',
};

if ($meldung === null && is_uploaded_file($datei['tmp_name'])) {
    $ziel = '/srv/app/uploads/' . bin2hex(random_bytes(16)) . '.pdf';
    move_uploaded_file($datei['tmp_name'], $ziel);
}
```

Exemplet visar bara mekaniken. Kontroll av filtyp, lagring utanför webbroten och spärr av skriptkörning beskrivs i guiden [Implementera filuppladdningar i PHP säkert](https://phpsos.de/sv/ratgeber/datei-upload-sicher/).

## Uppladdningar och PHP2Shell: vad de tillfälliga åtgärderna innebär

PHP2Shell-sårbarheterna CVE-2026-109618 och CVE-2026-109619 gäller bearbetningen kring förfrågan i PHP-tolken. Därför nämner php.net som tillfälliga åtgärder bland annat `post_max_size = 8M` (utan uppladdningar) eller `32M` (med uppladdningar), `upload_max_filesize` lägre än så, samt `session.upload_progress.enabled=0`. De här värdena hör hemma i php.ini, FPM-poolen eller serverkonfigurationen, därefter måste alla workers startas om.

Fackskribenter föreslår dessutom att `multipart/form-data`-förfrågningar utanför riktiga uppladdningssökvägar blockeras i proxyn eller WAF:en. Det är en inofficiell åtgärd med risk att formulär slutar fungera; avvägningen beskrivs i inlägget [Filtrera uppladdningsförfrågningar i kanten](https://phpsos.de/sv/blog/upload-anfragen-am-rand-filtern/). Ingen av de här åtgärderna ersätter uppdateringen till PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35.

## Hur hjälper PHP SOS konkret?

- Vi tar reda på vilka av era webbplatser som faktiskt behöver uppladdningar och på vilka sökvägar.
- Vi sätter gränsvärdena och de tillfälliga åtgärderna per pool, startar om alla workers och testar formulären.
- Vi kontrollerar uppladdningskataloger efter körbara filer och spärrar skriptkörning inom ramen för [serverhärdning](https://phpsos.de/sv/leistungen/server-haertung/).
- Vi installerar PHP-uppdateringen och dokumenterar patchläget per webbplats.


## Vanliga frågor

**Var sparar PHP uppladdade filer först?**  
I en temporär katalog. Som standard är det systemets temporära katalog; med upload_tmp_dir kan den ändras. Flyttas filen inte i skriptet raderar PHP den i slutet av förfrågan.

**Kan jag lita på fältet type i $_FILES?**  
Nej. MIME-typen rapporteras av webbläsaren, och PHP kontrollerar den enligt manualen inte. Även name och full_path kommer från avsändaren. Den faktiska filtypen fastställer ni på serversidan utifrån innehållet.

**Varför är $_FILES tom fast en fil skickades?**  
Oftast var hela förfrågan större än post_max_size. Då tömmer PHP $_POST och $_FILES helt, utan felkod i arrayen. Andra orsaker är ett saknat enctype i formuläret eller avstängda file_uploads.

**Hjälper det mot PHP2Shell att förbjuda uppladdningar i applikationen?**  
Nej. Bearbetningen av förfrågningsdata sker i PHP innan applikationskoden körs. Det som fungerar är PHP-uppdateringen och omstart av alla workers; snävare gränsvärden är bara en tillfällig åtgärd.

**Vad är skillnaden mellan upload_max_filesize och post_max_size?**  
upload_max_filesize begränsar varje enskild fil, post_max_size hela förfrågans innehåll inklusive alla filer och fält. post_max_size måste därför vara större än upload_max_filesize.

## Källor

- [PHP-manualen: Uppladdning med POST](https://www.php.net/manual/de/features.file-upload.post-method.php)
- [PHP-manualen: Felmeddelanden vid uppladdning](https://www.php.net/manual/de/features.file-upload.errors.php)
- [PHP-manualen: php.ini-direktiv](https://www.php.net/manual/de/ini.core.php)
- [php.net: Säkerhetsmeddelanden](https://www.php.net/)
