The Perl Base64 Module
MIME::Base64 has been core Perl since Perl 5.8 (2002). It exports two functions: encode_base64 and decode_base64. The module is MIME-centric by heritage — RFC 2045 required Base64 output to wrap at 76 characters per line for email bodies — so encode_base64inserts newlines by default. Almost every modern use case wants a single unbroken token, so you'll pass an empty EOL argument to suppress the wrapping.
Encoding a String — The Standard Pattern
#!/usr/bin/env perl
use strict;
use warnings;
use MIME::Base64;
# WARNING — default inserts "\n" every 76 characters (MIME style)
my $with_newline = encode_base64("Hello");
# "SGVsbG8=\n"
# Correct for modern use — pass "" as EOL
my $b64 = encode_base64("Hello", "");
# "SGVsbG8="
# Decode
my $decoded = decode_base64($b64);
# "Hello"
# Round trip a longer string
my $long = "The quick brown fox jumps over the lazy dog" x 10;
my $out = encode_base64($long, "");
die unless decode_base64($out) eq $long;Handling UTF-8 and Wide Characters
Perl distinguishes byte strings (the raw-byte interpretation Perl uses by default) from character strings (what you get after calling Encode::decode or reading from a :utf8handle). MIME::Base64 only understands byte strings. Passing a decoded character string triggers a "Wide character" warning and emits broken output.
use strict;
use warnings;
use MIME::Base64;
use Encode qw(encode);
# WRONG — Perl 5 complains about "Wide character"
my $text = "Hello 世界";
my $broken = encode_base64($text, ""); # emits warning, wrong bytes
# RIGHT — encode to UTF-8 bytes first
my $bytes = encode("UTF-8", $text); # raw byte string
my $b64 = encode_base64($bytes, "");
# "SGVsbG8g5LiW55WM"
# Round trip
my $raw = decode_base64($b64);
my $recoded = Encode::decode("UTF-8", $raw);
die unless $recoded eq $text;Encoding a File
use strict;
use warnings;
use MIME::Base64;
use File::Slurp qw(read_file); # or File::Slurper (modern)
# Small to medium files — one-shot
my $data = read_file("./image.png", binmode => ":raw");
my $b64 = encode_base64($data, "");
print "$b64\n";
# Big files — stream chunks (57 bytes = 76 output chars, matches MIME line length)
sub encode_file_streaming {
my ($path, $out_fh) = @_;
open my $in, "<:raw", $path or die "open: $!";
# Chunk size MUST be a multiple of 3 to avoid mid-stream padding
my $chunk;
while (read($in, $chunk, 3072)) {
print $out_fh encode_base64($chunk, "");
}
close $in;
}
# Build a data URL for HTML/CSS embedding
my $png = read_file("./icon.png", binmode => ":raw");
my $url = "data:image/png;base64," . encode_base64($png, "");URL-Safe Base64 for JWTs
MIME::Base64 has no URL-safe variant built in. Three options, in order of recommendation:
# Option 1 — CPAN module (clean, named functions)
# cpan MIME::Base64::URLSafe
use MIME::Base64::URLSafe;
my $jwt_segment = encode_base64url($payload);
my $decoded = decode_base64url($jwt_segment);
# Option 2 — translate the standard alphabet manually (JWT style)
use MIME::Base64;
sub b64url_encode {
my ($data) = @_;
my $b64 = encode_base64($data, "");
$b64 =~ tr{+/}{-_}; # URL-safe alphabet
$b64 =~ s/=+\z//; # strip padding (JWT convention)
return $b64;
}
sub b64url_decode {
my ($s) = @_;
my $t = $s;
$t =~ tr{-_}{+/};
$t .= '=' x (-length($t) % 4); # restore padding
return decode_base64($t);
}
# Option 3 — if you only need one direction, the raw translate is a 2-liner
my $jwt_part = encode_base64($payload, "");
$jwt_part =~ tr{+/=}{-_}; # merges padding-removal and alphabet swapHTTP Basic Auth Header
use strict;
use warnings;
use MIME::Base64;
use LWP::UserAgent;
sub basic_auth_header {
my ($user, $pass) = @_;
return "Basic " . encode_base64("$user:$pass", "");
}
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => "https://api.example.com/me");
$req->header(Authorization => basic_auth_header("admin", "secret123"));
my $res = $ua->request($req);
print $res->status_line, "\n";
# LWP has a shortcut too — ::credentials — but if you want to see the raw
# header value (e.g. for debugging / cURL recreation), the manual version
# is more transparent.Perl One-Liners for the Shell
# Encode a string (no trailing newline)
perl -MMIME::Base64 -e 'print encode_base64("Hello", "")'
# SGVsbG8=
# Decode a string
perl -MMIME::Base64 -e 'print decode_base64("SGVsbG8=")'
# Hello
# Encode a file from stdin (reads binary cleanly)
cat /path/to/file | perl -MMIME::Base64 -0777 -ne 'print encode_base64($_, "")'
# -0777 slurps the whole stream in binary-safe mode
# Decode a file from stdin
cat /path/to/encoded.txt | perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)' > out.bin
# Build an HTTP Basic Auth token in one line
perl -MMIME::Base64 -E 'say encode_base64("admin:secret", "")'
# YWRtaW46c2VjcmV0Legacy Perl 5.6 and Earlier
If you are stuck on Perl 5.6 or earlier (RHEL 4 era), MIME::Base64 is a CPAN module, not core. Install with cpan MIME::Base64or distribution-level packaging. For truly dependency-free code (shouldn't be needed on anything built this decade), you can hand-roll Base64 using pack/unpack with the u template, but the output requires post-processing — not worth the complexity.
Common Mistakes
- Forgetting the empty EOL argument. Default
encode_base64($x)inserts\nevery 76 chars. For JSON/URLs/headers you wantencode_base64($x, ""). - Passing a decoded Unicode string.Perl warns "Wide character in subroutine entry" and emits wrong bytes. Always
Encode::encode("UTF-8", $x)first for text data. - Reading files without
:raw. Default file-open applies a PerlIO layer that normalizes line endings — fatal for binary data. Useopen my $fh, "<:raw", $pathorbinmode $fh. - Streaming with chunks not divisible by 3. Each chunk produces independent Base64 with its own padding — concatenating gives junk mid-stream. Chunk size must be a multiple of 3 (e.g. 3072, 6144) OR you must concatenate ALL input bytes BEFORE encoding.
- Trailing newline in
encode_base64output breaking downstream parsers.APIs that expect strict Base64 (no whitespace) reject the default\n. Always use the empty EOL argument when the output goes anywhere except an email body.
Key Facts
- Module:
- MIME::Base64 (core since Perl 5.8)
- Encode:
- encode_base64($data, $eol)
- Decode:
- decode_base64($b64)
- EOL argument:
- default "\n" (MIME); pass "" for single-line output
- Input type:
- byte string (encode UTF-8 first for Unicode text)
- URL-safe module:
- MIME::Base64::URLSafe (CPAN) or manual tr///
- Streaming:
- chunk size must be a multiple of 3 bytes
Related Base64 Tools
- Base64 Encode Online — general-purpose encoder
- Base64 Encode String — cross-language reference
- Base64 Encode in Bash — same sysadmin audience, no Perl needed
- URL-Safe Base64 — JWT and query string variant
- Base64 Decode Online — reverse the encoding
- JWT Debugger — inspect JWT tokens