UTF-8 all the way through: a complete guide for PHP developers

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why your Cyrillic turns into "Привет" and how to fix it once and for all

\n\n

Have you ever opened your PHP website and instead of the familiar "Hello, world!" seen something like Привет, РјРёСЂ!? If so — congratulations, you've encountered the classic UTF-8 all the way through problem. This is that situation where the encoding breaks somewhere along the path from the database to the browser, and everyone is to blame, but no one wants to take responsibility.

\n\n

On Stack Overflow, this question has gathered thousands of views and hundreds of answers. And for good reason. Even experienced developers sometimes get confused with encoding settings. In this tutorial, we'll break down a step-by-step recipe for making PHP, MySQL, and the browser work together so that UTF-8 works everywhere and always. No magic — just the right settings.

\n\n

Step 1. Database: the foundation of your encoding

\n\n

It all starts with the database. If your MySQL stores data in latin1 or cp1251, and you try to output it in UTF-8 — you'll get mojibake. But even if the database is in UTF-8, there's a nuance: the old utf8 encoding in MySQL does not support emoji characters and some rare hieroglyphs. The solution is utf8mb4.

\n\n

1.1. Creating tables with the correct encoding

\n\n

When creating a new table, always specify:

\n\n
CREATE TABLE articles (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    title VARCHAR(255) NOT NULL,\n    content TEXT NOT NULL\n) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
\n\n

An alternative is to set the default encoding for the entire database:

\n\n
ALTER DATABASE your_database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
\n\n

Important: utf8mb4_unicode_ci is a collation (comparison rule). It correctly handles all Unicode characters. If you don't need sorting by different languages, utf8mb4_general_ci will also work.

\n\n

1.2. Converting existing tables

\n\n

If you already have data in a different encoding, first convert it:

\n\n
ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
\n\n

But be careful: if the table already had data in latin1, it may turn into double encoding (e.g., Привет). In that case, you first need to convert the columns to binary, and then to utf8mb4. That's a separate story, but for simplicity, let's assume you're starting from scratch.

\n\n

Step 2. PHP database connection: configuring PDO

\n\n

In modern PHP, you use PDO (or mysqli). And here's a critical point: after connecting to the database, you need to explicitly specify the encoding. Otherwise, PHP will communicate with MySQL in the default encoding (often latin1).

\n\n

2.1. Correct PDO connection string

\n\n
$host = 'localhost';\n$dbname = 'your_database';\n$username = 'root';\n$password = '';\n\n$dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";\n\n$options = [\n    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n    PDO::ATTR_EMULATE_PREPARES => false,\n];\n\ntry {\n    $pdo = new PDO($dsn, $username, $password, $options);\n} catch (PDOException $e) {\n    echo "Connection failed: " . $e->getMessage();\n}
\n\n

Notice the charset=utf8mb4 in the DSN. This forces PHP and MySQL to agree on the encoding at the connection level.

\n\n

2.2. Alternative method (if DSN doesn't work)

\n\n

Sometimes older versions of PHP or servers don't read the charset from the DSN. In that case, add this after connecting:

\n\n
$pdo->exec("SET NAMES 'utf8mb4'");
\n\n

But the first method is preferable — it's cleaner and more modern.

\n\n

Step 3. HTTP headers and HTML: telling the browser the truth

\n\n

Even if the database and PHP work in UTF-8, the browser might decide that

Blogs

Book Recommendations