建安四年(199年)六月,袁紹起兵十萬,戰馬萬匹,企圖南下進攻許昌。曹軍諸將認為難以戰勝袁紹,曹操卻說:「吾知紹之為人,志大而智小,色厲而膽薄,忌克而少威,兵多而分畫不明,將驕而政令不一,土地雖廣,糧食雖豐,適足以為吾奉也。」遂決定起兵兩萬迎敵。
八月,曹操率軍進據黃河北岸重鎮黎陽。派臧霸率精兵入青州一帶,防止袁軍從東面襲擊許昌。留於禁屯兵延津,令劉延守白馬,以防止袁軍渡河南下,此為第一道防線。九月,曹操還許昌,置主力在官渡筑壘固守,為第二道防線。官渡為袁紹進攻許昌的咽喉之地。12月,曹操復還官渡,親自坐鎮,嚴陣以待袁軍來犯。
正當曹操全力以赴佈置對袁作戰時,劉備起兵反叛曹操,殺曹徐州刺史車冑,佔據下邳,屯兵沛縣,並積極與袁紹聯絡,欲合力夾攻曹操。曹操分析了眼前的形勢,認為「夫劉備,人傑也,今不擊,必為後患。袁紹雖有大志,而見事遲,必不動也」。遂於建安五年(200年)年正月,率精兵東征劉備,一舉佔領沛縣,收復徐州,轉攻下邳,並迫降了關羽。劉備全軍潰敗,僅帶少數兵馬逃往河北投奔袁紹去了。曹操獲勝後,還軍官渡,以迎戰袁紹。
建安五年(200年)二月,袁紹親率大軍進駐黎陽,派遣郭圖、淳於瓊、顏良進攻東郡太守劉延駐守的白馬,劉延兵勢微弱,被顏良軍圍困,情況緊急。四月,曹操決定率兵救劉延。他採納謀士荀攸的建議,先自引兵到延津,佯裝渡河襲擊袁紹的後方,袁紹當即分兵西應。曹操卻乘機率一支精兵向東奔襲白馬,顏良措手不及,被關羽斬殺,白馬之圍遂被解出。袁紹又派大將文丑與劉備率五六千騎步兵渡河追擊曹軍。曹軍當時只有騎兵五百餘騎,危急之中,曹操令眾騎兵解鞍放馬,又將輜重丟棄在袁軍來的路上。不久,文丑兵到,見到曹軍丟棄的馬匹、輜重後,你爭我搶,亂作一團。曹操見此,命其500多騎兵立即上馬,向袁軍衝殺,袁軍頓時潰敗,大將文丑被斬首。此番曹軍連斬顏良、文丑兩員河北名將,袁軍大為震動。袁紹下令退軍陽武。曹操也還軍官渡固守。
八月,袁紹兵臨官渡,依沙堆立營,東西數十里。曹操也分營與袁軍對峙。九月,曹軍出擊,但戰鬥不順。當時曹操兵不滿一萬,且十有二三為傷兵,只好退回營壘堅守。 袁軍見此,便堆起土山,筑高櫓,用箭俯射曹營,使曹軍士兵只能蒙盾而行。為了扭轉這種被動局面,曹操命工匠連夜趕造霹靂車,向袁軍還以飛石,摧毀了袁軍的櫓樓。 袁紹又命士卒挖地道襲擊曹營,曹軍也針鋒相對,在營內掘地壕以對抗。曹操又聞袁紹的幾千車軍糧即將運至官渡大營,他立刻派大將徐晃、史渙截擊,將袁軍糧草全部燒燬。
雙方相峙月餘,曹軍糧草將盡,士卒疲乏,於是曹操尋機與袁紹決戰。建安五年(200年)十月,袁紹又派車運糧,命淳於瓊等五人率兵萬餘人護衛,將糧屯積在袁軍大營以北四十里的烏巢。謀臣許攸建議袁紹派輕騎趁夜突襲許昌,袁紹不予採納。恰這時許攸家中有人犯法,被其政敵審配扣押。許攸一怒之下,投奔曹操,並獻計曹操奇襲烏巢。許攸之計正合曹操尋找戰機,出奇制勝的心意。於是曹操當機立斷,留曹洪、荀攸守官渡大營,自己親率步騎五千人,連夜出發,一路扮著袁軍,騙過袁軍哨卡。到達烏巢後,即圍住糧屯放火,及至天明。淳於瓊才見曹軍兵少,於是出壘迎戰。曹操揮軍衝殺,袁軍抵擋不住,被迫推回營壘堅守。這時,袁紹一面派騎兵救烏巢,一面命張合阝、高覽攻打曹軍官渡大營,企圖使曹操退無所歸。當增援的袁軍騎兵逼近烏巢時,左右報請曹操分兵抵擋,曹操大怒說:「敵兵到了我背後才再告訴我!」於是士卒皆殊死拚殺,大破烏巢守軍,擒殺袁將淳於瓊。攻打曹軍大營的張合阝、高覽二將聞淳於瓊兵敗,又聞袁紹對他們二人起疑心,遂於陣前投降曹操。曹操乘勢揮軍掩殺,袁軍大潰,袁紹及其兒子袁譚只帶了八百餘騎,倉惶逃往河北。
官渡之戰,經過一年多的對峙,至此以曹操的全面勝利而告結束。曹操以兩萬左右的兵力,出奇制勝,擊敗袁軍十萬。這個戰例成為中國歷史上以弱勝強,以少勝多的典型戰例。曹操以其非凡的才智和勇氣,寫下了他軍事生涯最輝煌的一頁。建安七年(202年),袁紹因兵敗憂鬱而死,曹操乘機徹底擊滅了袁氏軍事集團,建安十二年(207)年,曹操又征服烏桓,至此,戰亂多時的北方實現了統一。