Inter had to win on the final day to guarantee a top-four finish and on a night of high drama at San Siro they also condemned Empoli to relegation.
Belgium midfielder Nainggolan struck with nine minutes remaining after Empoli's Hamed Junior Traore had cancelled out Keita Balde's opener.
Inter striker Mauro Icardi's second-half penalty was saved, team-mate Marcelo Brozovic's stoppage-time goal was chalked off by the video assistant referee and both sides had a player sent off in added time.
The Nerazzurri's victory means Luciano Spalletti's side finished one place and a point above city rivals AC Milan to snatch the final Champions League berth.
Empoli, who started one place above the relegation zone and needed to better Genoa's result, were relegated as a consequence of the latter's goalless draw at Fiorentina.
Empoli goalkeeper Bartlomiej Dragowski kept Inter at bay up until at half-time with a string of outstanding saves.
The Poland Under-21 international, on loan from Fiorentina, twice kept out long-range efforts from Nainggolan and another from Ivan Perisic.
Brozovic, Matias Vecino and Kwadwo Asamoah also had goalbound efforts from outside the penalty area turned away at full stretch, while Stefan De Vrij's goalbound header was tipped over the crossbar.
The first period was mainly one-way traffic, but in between Dragowski's heroics, Empoli pair Francesco Caputo and Traore wasted goalscoring chances.
Spalletti replaced Asamoah with Keita during the interval and the substitute broke the deadlock six minutes after the restart.
Matteo Politano teed him up on the edge of the area and the Senegal forward drilled a low angled shot beyond Dragowski and into the bottom corner.
Eight minutes later Dragowski brought down Icardi in the box, but then comfortably saved the Argentinian's poor penalty after the incident had been referred to the video assistant referee.
Keita and Nainggolan both went close to extending Inter's lead before Empoli served warning through Cristian Dell'Orco's header.
Traore then tapped home Salih Ucan's cross from close range to silence San Siro with 14 minutes left.
At that stage, Empoli were safe and Inter were losing out on a Champions League place to city rivals Milan, but Nainggolan came to the rescue five minutes later.
Inter caught Empoli flat-footed on the counter-attack and, after Vecino's shot hit a post, Nainggolan kept his composure to turn home the rebound.
In a frantic finish, Brozovic's stoppage-time goal was ruled out after video replays showed Keita pulling back Dragowski in the build-up.
Empoli's Filippo Perucchini was shown a straight red card for remonstrating with the referee and there was still time enough for Keita to be shown his second yellow card in the seventh minute of time added on.