Skip to content
ASCII World

Line Endings Explained: CR, LF, and CRLF

How Typewriter Hardware Created Today's Text Interoperability Bugs

By the ASCII World team

Mechanical Teletypes and the Physical Split of CR and LF

Teletype Model 33 machines in the early 1960s required roughly 200 milliseconds to reposition their heavy printing carriage back to the left margin. During that physical movement, sending another printable characters caused the mechanical head to print characters mid-flight across the page, smearing ink diagonally across the paper. The mechanical sequence required two separate signals: Carriage Return (CR, 0x0D or decimal 13) moved the cylinder horizontally back to position zero, while Line Feed (LF, 0x0A or decimal 10) rotated the platen vertically by one line position.

Telecommunication engineers included both commands in the ASCII table standard ASA X3.4-1963. In early electromechanical terminals, sending CR followed by LF allowed the motor enough time to complete physical carriage travel while paper advance commenced. Omitting either byte caused hardware jamming or distorted printouts. Teletype operators sometimes inserted extra null bytes after a CR on high-speed lines to give the carriage sufficient settling time before printing resumed.

The distinction between the two control characters remains visible in low-level byte streams:

Character Acronym Escape Sequence Decimal Value Hexadecimal Value ASCII Keyboard Shortcut
Carriage Return CR \r 13 0x0D Ctrl+M
Line Feed LF \n 10 0x0A Ctrl+J

Operating System Split: Unix, MS-DOS, and Classic Mac OS

Multics developers in the late 1960s decided that storing two bytes for every line ending wasted precious main memory and disk capacity. When Dennis Ritchie and Ken Thompson created Unix at AT&T Bell Labs, they adopted the Multics convention of using a single Line Feed (0x0A) to mark the end of a line in text files. The Unix TTY terminal driver translated this single character into the two-byte CR-LF sequence at runtime whenever text was output to physical teletype hardware. Software applications stored clean single-byte line terminators in memory while hardware drivers handled physical device quirks.

Gary Kildall designed CP/M in 1974 for 8-bit Intel 8080 microcomputers. CP/M maintained direct compatibility with Teletype terminals by storing line endings as explicit CR-LF byte pairs in text files. When Tim Paterson wrote QDOS in 1980 (which Microsoft acquired and renamed MS-DOS 1.0), he replicated CP/M file system behavior and system call conventions to ensure existing CP/M software like WordStar could be ported easily. Microsoft Windows retained MS-DOS file system conventions, making \r\n the standard text line terminator across all 16-bit, 32-bit, and 64-bit Windows releases.

Apple took a third path with the Macintosh in 1984. Apple engineers picked Carriage Return (0x0D) alone as the end-of-line character for System 1 through Mac OS 9. When Apple released Mac OS X in 2001 based on the NeXTSTEP architecture and BSD Unix kernel, Apple abandoned the CR-only convention and standardized on Unix LF (0x0A). Legacy Classic Mac text files remain the primary source of lone CR bytes in modern software development.

Network Protocols: Why the Internet Standardized on CRLF

Early network protocol authors required a single canonical line representation so heterogenous computer architectures could exchange commands reliably across the ARPANET. RFC 822 (published in 1982 for internet text messages) and RFC 959 (FTP) specified that control lines and ASCII data streams must terminate with explicit CRLF byte pairs regardless of the operating system operating at either endpoint.

HTTP maintained this requirement in RFC 2616 and updated specifications like RFC 9110. An HTTP response header block must end with a CRLF sequence, and an empty CRLF line separates the HTTP headers from the response payload body:

HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 42\r\n\r\n<html><body>Hello World</body></html>

Web servers parsing incoming request headers look for the byte values 0x0D 0x0A 0x0D 0x0A to identify the exact boundary where header metadata ends and payload content begins. Web servers using strict parsing fail or return HTTP 400 Bad Request responses when clients send headers terminated only with LF. Security vulnerabilities like HTTP Response Splitting occur when software applications fail to sanitize user inputs containing embedded CRLF sequences, allowing attackers to inject arbitrary HTTP headers or fabricate duplicate responses.

Common Cross-Platform Failures and Diagnostic Commands

Opening a Windows-created shell script on a Linux server frequently results in mysterious script execution errors. When bash reads a file containing Windows CRLF line endings, it interprets the \r byte as part of the command string rather than whitespace formatting. A script starting with #!/bin/bash\r causes Linux kernel script loaders to search for an interpreter named /bin/bash\r on disk, returning errors such as No such file or directory or syntax error near unexpected token '$'\r''.

Software developers inspect line endings using command-line utilities that expose non-printable control byte values in hexadecimal or visual escape notations:

  • file: The Linux file textfile.txt command reports ASCII text, with CRLF line terminators if CR bytes are present.
  • hexdump: Running hexdump -C textfile.txt displays raw byte values in memory. A line ending showing 0d 0a confirms CRLF, while 0a confirms LF.
  • cat: The command cat -v textfile.txt displays CR bytes as ^M visual control characters at the end of every line.
  • dos2unix: Executing dos2unix filename strips all 0x0D bytes directly preceding 0x0A bytes in place.
  • unix2dos: Executing unix2dos filename inserts a 0x0D byte before every isolated 0x0A byte.

Reading raw byte structures helps clarify how computers store text from bits to characters across varying system architectures and ASCII standard implementations.

Language Parsers, I/O Buffers, and Git Handling

Programming languages abstract physical line ending differences using platform-dependent text stream drivers. The C standard library function fopen() distinguishes between text mode ("r") and binary mode ("rb"). On Windows platforms using Microsoft Visual C++ runtime libraries, reading a text file opened in "r" mode automatically strips \r bytes from incoming streams, presenting C code with isolated \n characters in buffer memory. Writing \n to a text mode file stream automatically injects \r\n onto the physical disk. Opening a file in "rb" binary mode bypasses this automatic translation layer, reading the exact on-disk byte sequence into memory.

Python 3 manages line endings using the universal newlines parameter inside its built-in open() function. By default, open(file, mode='r', newline=None) converts \r\n, \n, and \r inputs into standard \n string characters inside application code. Setting newline='' disables translation, leaving line terminator bytes unchanged. Developers converting raw encoding formats rely on dedicated text converter tools when raw bytes must be verified prior to parsing.

Version control systems like Git manage cross-platform line termination through repository settings. When multiple developers work on the same repository using different operating systems, unmanaged line endings cause Git to report every line in a file as modified despite no visible content changes. Git provides the core.autocrlf configuration setting to standardize line endings in repository trees:

  • git config --global core.autocrlf true: Windows developers use this setting to convert LF line endings to CRLF during repository checkout, while converting CRLF back to LF during code commit operations.
  • git config --global core.autocrlf input: Linux and macOS developers use this setting to convert any accidental CRLF line endings to LF on commit while keeping local repository checkouts as LF.
  • git config --global core.autocrlf false: Disables automatic conversion, preserving byte streams exactly as authored.

Project teams enforce consistent line endings by creating a .gitattributes file at the root of a Git repository. Adding the line * text=auto forces Git to normalize all text files to LF within the remote repository object database, while permitting local operating system checkouts to adapt as specified.

References

  1. ANSI X3.4-1986: American National Standard for Information Systems - Coded Character Sets - 7-Bit American National Standard Code for Information Interchange (ASCII)
  2. RFC 2821: Simple Mail Transfer Protocol (SMTP)
  3. RFC 9110: HTTP Semantics (Field Syntax and Line Termination)
  4. ISO/IEC 646:1991: Information technology - ISO 7-bit coded character set for information interchange