Отправка файлов через Discord Webhook (изображения, вложения, multipart)
Загрузка файлов через Discord webhook с multipart/form-data. Рабочие примеры на Python, JavaScript, curl и PHP — изображения, PDF, логи, вложения в embed.
Discord webhook’и не только для текста. Можно загрузить скриншоты, лог-файлы, PDF, архивы — что угодно до 25 МБ (лимит free-tier 2026). Подвох: нужно перейти с JSON на multipart/form-data. Если формат неверный — Discord молча отклоняет загрузку с общим 400.
В гайде — точная форма запроса и рабочий код для Python, JavaScript, curl и PHP, включая прикрепление файла внутрь embed’а как изображения.
Как работает загрузка файлов
Обычно вы шлёте application/json:
{ "content": "Hello" }
Для файлов — multipart/form-data с двумя частями:
payload_json— обычный webhook JSON (content, embeds, username и т. д.)files[N]— бинарный контент файла, индекс с0
Тот же JSON, что обычно, идёт внутри payload_json; файл — рядом.
Лимиты файлов (2026)
| Tier | Макс. файл | Файлов на сообщение |
|---|---|---|
| Free | 25 МБ | 10 |
| Nitro Basic | 50 МБ | 10 |
| Nitro | 500 МБ | 10 |
| Boost (сервер) | 50–100 МБ от уровня | 10 |
Webhook использует boost-tier сервера. Webhook в Level 3 сервере шлёт 100 МБ, даже если у вас free-аккаунт.
Python — requests
import requests
WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"
with open("screenshot.png", "rb") as f:
files = {
"file": ("screenshot.png", f, "image/png"),
}
payload = {
"payload_json": '{"content": "Артефакт билда:", "username": "CI Bot"}'
}
r = requests.post(WEBHOOK_URL, data=payload, files=files)
r.raise_for_status()
Ключевое:
payload_json— это строка (не dict) внутриdata- MIME-тип важен —
image/png,application/pdf,text/plainи т. п. requestsсам строит multipart boundary
Несколько файлов
files = {
"files[0]": ("error.log", open("error.log", "rb"), "text/plain"),
"files[1]": ("trace.txt", open("trace.txt", "rb"), "text/plain"),
"files[2]": ("dump.bin", open("dump.bin", "rb"), "application/octet-stream"),
}
requests.post(WEBHOOK_URL, data={"payload_json": '{"content": "Crash report"}'}, files=files)
Ключи должны быть files[0], files[1], … по порядку.
JavaScript — Node.js FormData
В Node 18+ FormData и fetch встроены:
import fs from 'node:fs';
const form = new FormData();
form.append('payload_json', JSON.stringify({
content: 'Прикреплён ежедневный отчёт',
username: 'Reports Bot',
}));
const buffer = fs.readFileSync('./report.pdf');
form.append('file', new Blob([buffer], { type: 'application/pdf' }), 'report.pdf');
const res = await fetch(process.env.WEBHOOK_URL, {
method: 'POST',
body: form,
});
console.log(res.status);
Content-Type вручную не ставь — fetch сам выведет multipart boundary из FormData.
Загрузка из браузера (без бэкенда)
Если разрешаете загрузку из <input type="file">:
async function uploadToDiscord(file, message) {
const form = new FormData();
form.append('payload_json', JSON.stringify({ content: message }));
form.append('file', file, file.name);
const res = await fetch(WEBHOOK_URL, { method: 'POST', body: form });
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
}
document.querySelector('input[type=file]').addEventListener('change', async (e) => {
await uploadToDiscord(e.target.files[0], 'Новая загрузка с сайта');
});
Внимание: не светите webhook URL в клиентском коде, если не готовы, что им может пользоваться кто угодно. В проде проксируйте через бэкенд.
curl — командная строка
curl -X POST \
-F "payload_json={\"content\":\"Прикреплён лог\"}" \
-F "file=@./server.log;type=text/plain" \
https://discord.com/api/webhooks/ID/TOKEN
Кавычки важны: JSON внутри payload_json нужно экранировать в shell. Для сложного JSON удобнее писать во временный файл:
echo '{"content":"Лог","embeds":[{"title":"Build #42","color":3066993}]}' > /tmp/p.json
curl -X POST \
-F "payload_json=<@/tmp/p.json" \
-F "file=@./build.log" \
https://discord.com/api/webhooks/ID/TOKEN
PHP — CURLFile
<?php
$webhook = 'https://discord.com/api/webhooks/ID/TOKEN';
$payload = json_encode([
'content' => 'Прикреплён ежедневный бэкап',
'username' => 'Backup Bot',
]);
$ch = curl_init($webhook);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'payload_json' => $payload,
'file' => new CURLFile('/srv/backups/db.sql.gz', 'application/gzip', 'db.sql.gz'),
]);
$response = curl_exec($ch);
curl_close($ch);
CURLFile сам ставит правильный MIME boundary; не используйте @filename-строки (deprecated и небезопасно с PHP 5.6).
Изображения внутри embed
Хотите, чтобы файл показался внутри embed, а не отдельным вложением? Ссылайтесь через attachment://:
import requests
with open("chart.png", "rb") as f:
files = {"file": ("chart.png", f, "image/png")}
payload_json = '''
{
"embeds": [{
"title": "Q4 Revenue",
"image": { "url": "attachment://chart.png" }
}]
}
'''
requests.post(WEBHOOK_URL, data={"payload_json": payload_json}, files=files)
Имя файла в attachment://chart.png должно точно совпадать с именем в multipart-части. Работает для image, thumbnail, author.icon_url, footer.icon_url.
Стриминг больших файлов
Для файлов под 25 МБ — стримите с диска, чтобы не грузить всё в память:
import requests
def upload_large(path: str, message: str):
with open(path, "rb") as f:
files = {"file": (path.split("/")[-1], f)}
return requests.post(
WEBHOOK_URL,
data={"payload_json": f'{{"content": "{message}"}}'},
files=files,
timeout=60, # увеличить таймаут на больших файлах
)
upload_large("/var/log/app/2026-04.tar.gz", "Месячные логи")
Файлы со спойлером
Префикс SPOILER_ в имени файла = Discord размывает превью:
files = {"file": ("SPOILER_screenshot.png", open("screenshot.png", "rb"), "image/png")}
Полезно для скриншотов с чувствительными данными — открыть можно, но автопревью не палит.
Частые ошибки
| Статус | Причина | Решение |
|---|---|---|
400 + Request entity too large | Файл больше лимита | Сжать, разрезать или поднять boost |
400 + ошибка payload_json | Невалидный JSON внутри | Сначала валидировать через json.dumps() |
| 413 | Серверный лимит размера | То же, что 400 — уменьшить файл |
| 415 | Неверный content-type | Не ставь Content-Type: application/json для файлов |
| 200 но файл не появился | Не совпадает имя файла с attachment:// | Точное совпадение имени с расширением |
Попробуйте вживую
Конструктор Discord Webhook поддерживает drag-drop файлов в сообщение и показывает превью точно как будет в Discord — тот же multipart, что описан выше. Для других рецептов смотрите гайды Python и JavaScript.
Источники
- Discord docs: Uploading Files
- Discord docs: Execute Webhook
Попробуйте в нашем инструменте
Открыть конструктор Discord Webhook